-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Proposal: Add Support for Ad-hoc existential Types #12937
Comments
Makes me think of roles dotnet/csharplang#1711 |
See also dotnet/csharplang#1328 |
It would be nice to hear where you see the connection. On parts of how it could get implemented or on a conceptional level?
On a conceptional level, I think they are quite different: Roles are introducing new nominal types and allow to add members to existing types, ad-hoc existential types don't add new named types and don't allow to add members to existing types.
Interesting read! And another take on what existential types can look like. Interestingly a completely different use-case where it's about type crafting. Where here it's more about being able to pipe data in a way that you don't lose type information. Note: in the above code one could just use dynamic as a replacement for the QUESTIONABLETYPE and it would work. But that's like opting out of the static type system and by that not a fair suggestion for anyone interested in static typing and generics. |
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process. This process is part of our issue cleanup automation. |
This issue will now be closed since it had been marked |
Being a proponent of static typing and generics, mainly for the reason of safety and code readability, I have to admit that I sometimes find it hard to understand when to actually embrace generics and when to keep things simple.
Let's pretend I'm not the only one that sometimes is unsure about the best design. And let's pretend we want to use a third party library where the library designer decided to make use of generics:
we could introduce our own types that implement the required interfaces and all will work out fine.
Now, there are quite some problematic scenarios that we could come up with.
Let's pretend that we want to mix instances of ABC and ABD in a collection and then loop over them and call ConsumeAB(item);
What's the type of that collection?
Or let's say that we want to design a method that hands us a value that can be consumed by ConsumeAB();
If you regard this as a legitimate problem, let me go on. Otherwise please tell me why this is not legitimate. I have the feeling that the type system is a tad unbalanced in a way that it allows to express constraints on the consumer side easier than being able to fulfill them and provide data of a type that matches the constraints.
My take on it is this: The method
ABProducer.ProduceOneByChance();
should be able to output an ad-hoc existential type. It should be able to tell us "there exists a type that supports IA and IB and I'll hand you a value of such a type, but I can't tell you which type it is. It might be ABC or ABD or something you never heard of..." So it is not a type parameter in a sense that the caller would be allowed to hand over a type argument of any type that supports IA and IB.It is more something like
∃T: IA, IB
or in other words: it is an ad-hoc type intersection: something that supports IA and IB. And to avoid the notion of a type parameter waiting to be replaced by a type argument, let's just think of it like this:(IA & IB)
.so this is still our library function. We are unable to change the signature of the method.
but
(IA & IB)
is assignment compatible:This type would only be existent at compile time. The JIT compiler would be aware of it and would be able to check type safety. At runtime, however, we'd see instances of ABC (...): the concrete non-abstract types. So basically we just introduced anonymous, transparent, ad-hoc abstract data types, that are unlike interfaces as they don't appear in the supertype list of the concrete types, but share a certain aspect with interfaces: they are abstract data types and therefore
GetType()
will never report such a type.And I think this would be a reason to be very happy as all sorts of equality and identity issues do not arise. No wrappers involved...
Now, what's the output type of
ProduceOneByChance
?Here are two options:
it would be great if both would be possible.
Let's call
(ABC | ABD)
an ad-hoc type union for now, which is not the same as a union type like seen in F#. Again at runtime, these types don't exist. AgainGetType()
will return ABC or ABD.We could also imagine to directly work with the union type like this:
But these are language details and optional.
The most important point of the proposal is that none of this can be done without some support by the underlying type system.
This proposal is about symmetry. We have to admit that we have a yet unbalanced type system that allows us to introduce constraints that have the potential to turn beautiful precise code into a barely usable piece of functionality as we can't fulfill the constraints in certain cases.
Note: Working around the issue by introducing a wrapper type that supports the needed interfaces is not the same. The consuming method might still want to check for certain additional interfaces like seen here: https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/Count.cs
By building a wrapper that supports the needed interfaces IA and IB we lose the runtime type information and a check for IC and ID will always fail. We'd also need to get dirty and rethink equality and identity when comparing original and wrapped objects in certain scenarios.
And suddenly we are just fighting with the type system, which is not its original purpose.
The text was updated successfully, but these errors were encountered: