Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polymorphic interfaces #204

Merged
merged 4 commits into from
Jul 16, 2018
Merged

Polymorphic interfaces #204

merged 4 commits into from
Jul 16, 2018

Conversation

RikkiGibson
Copy link
Member

Fixes Azure/azure-sdk-for-node#2960

We're not 100% sure this is the approach we want, but we have played with a bunch of different possible representations and this so far seems to have the best editor experience.

@RikkiGibson RikkiGibson requested review from amarzavery and a user July 12, 2018 18:48
/**
* @member {string} fishtype Polymorphic Discriminator
*/
fishtype: "Fish";
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I inserted a .OrderBy in the generator to make the discriminator always the first property in the polymorphic interface. I think this is a better user experience.

}

/// <summary>The name to use when referring to this type in a parameter, return type or property definition.</summary>
public string ReferenceName => PolymorphicUnionCases.Any() ? Name + "Union" : Name.ToString();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe call this UnionTypeName? It's basically saying that if this type is part of union, then use this type's name with the "Union" suffix. Otherwise, just use this type's name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might make sense. I thought the name conveyed the purpose: when referencing this type in an annotation, use this ReferenceName, and when declaring it or referencing it in a mapper, use Name. But it's a little contrived no matter what you do.

CodeModel.ModelTypes
.Where(m => m.BaseModelType == this)
.Cast<CompositeTypeTS>()
.ToList();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this only looks for types that derive from this type, right? This won't include this type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is so you can type FishUnion = Fish | SalmonUnion | SharkUnion-- by treating "this type" as a special case, you can make sure you reference the interface instead of the tagged union.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think this would be better named... ImmediatePolymorphicSubtypes or something..

@@ -33,6 +33,35 @@ public override Property Add(Property item)
return result;
}

private List<CompositeTypeTS> polymorphicUnionCases;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a List? Is it going to be modified after it is initially created?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like for it to be immutable, computed lazily on the first get and then cached. This is used in ReferenceName to decide whether to refer to the type as FooUnion or just Foo based on whether any polymorphic subtypes exist.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that, but why is it a List and not an Array or an IEnumerable?

Copy link
Member Author

@RikkiGibson RikkiGibson Jul 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not IEnumerable because I want it to be clear that "work" isn't being done upon enumeration.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's the case, then why not an array? Also, why does the laziness of the work matter to you? Isn't your only requirement that it be a sequence of values?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be an array. We can convey even more that you're not meant to overwrite values in the array by using IReadOnlyList. I just want to avoid repeated execution of the query through multiple usages of ReferenceName.

private List<CompositeTypeTS> polymorphicUnionCases;

/// <summary>Contains the immediate polymorphic children of this class.</summary>
public List<CompositeTypeTS> PolymorphicUnionCases
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a List? Are you expecting the caller of this property to add or remove values from this List?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. Why isn't this an IEnumerable?

@RikkiGibson
Copy link
Member Author

Updated a few things based on feedback--see what you think. I'm not adamantly against the PolymorphicUnionCases (which I maybe should rename) being array or IEnumerable, just want to avoid a trap I've gotten into in the past where an expensive query I enumerated multiple times slowed things down.

@ghost
Copy link

ghost commented Jul 12, 2018

I understand your performance concern. My biggest frustration with AutoRest is that the model that is presented passes through several layers that modify the original swagger specification, and it's difficult for me to keep track of what changes occurred where. I lean more towards immutable models that the generator can do queries and computations on and then store the results of those queries and computations in local variables for performance needs. However, I appreciate the IReadOnlyList as a compromise between our priorities.

@RikkiGibson
Copy link
Member Author

🤖 AutoRest automatic bundle size check 🤖

Size increased by 0.00% (0 B)

@RikkiGibson
Copy link
Member Author

After speaking with TS team it seems like this is probably a reasonable design--we could consider factoring out AnimalBase with all properties except the discriminator, but users who look at these generated interfaces will have to contend with all these contortions around it--Animal, AnimalUnion, AnimalBase... etc.

Keeping that to a minimum is probably more desirable, and it sounds like we might be able to get a fix in the TS go-to-definition behavior that makes it so the candidate symbols are further constrained by a tagged union discriminator, which pretty much eliminates our remaining concerns about this approach.

@RikkiGibson
Copy link
Member Author

Rebased to ensure compatibility with recent changes

@RikkiGibson RikkiGibson merged commit 13f71cf into master Jul 16, 2018
@RikkiGibson RikkiGibson deleted the PolymorphicInterfaces branch July 16, 2018 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant