-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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] defined some "sort of interfaces" not implemented by the type #6638
Comments
No I don't want to use dynamic for performance aspect. At compile time, the compiler knows which types inherit of SyntaxNode (in my sample) and can determine which types have the members defined in my pseudo-interface and it can use it. However, as it is a huge impact because it's introduce a notion of "pseudo-type" with some strange conversion, an other option may be that the compiler generates an intermediate solution like this:
and replace my "var n = node as SyntaxNodeWithToken;" by this:
I think this last solution won't be so hard to implement. What do you think? |
I think this could be done with a method generic constraint (#129)
|
@MatthieuMEZIL I don't see how could that work in the general case, when the classes are in different assemblies. When compiling your (BTW, could you format code in your posts? It would make them much easier to read.) |
I have written something very similar to this more times than I care to admit:
|
@alrz : not in this case. |
@svick: As a new feature, you can define any restriction you want. Think about partial method. They are only private because of the implementation. It would be too complex to allow protected partial method for example. |
@bbary : I want to avoid reflection. |
You don't need it, this is already possible in F# inline functions with member constraints
But it has to be inline, because it wouldn't be possible if this member is from different classes, as @svick said. Compiler must know what method is called inside the function at compile-time (to avoid any kind of reflection). |
Agreed, I don't want to do it with reflection either. I do it today with reflection because it provides me with a syntax that isn't terrible and I can afford the costs (after playing around with dynamic, I think I will be doing that instead from now on; one of the best things about reading random issues on github projects like this is learning things I didn't know before). Even with dynamic being surprisingly fast at this, I would still prefer some compile time safety. |
After thinking more about it, I don't think my intermediate solution is acceptable. Indeed, if we have thousands of sub types, performance adding thousands as maybe worse than using reflection... |
Unless I completely misunderstand what you are trying to do, it looks like you want structural types. C# has what is called a nominative type system, i.e. a type system where the relationships between types (compatibility, subtyping, etc.) are based on the names of the types. The basic idea of structural types is that relationships between types are based on the structure of the types. Two types are compatible if they have the same structure. What exactly is considered to be part of the structure, differs between languages, e.g. whether or not for record types, the names of the fields are considered to be part of the structure as well or only the types. E.g. in some languages The most prominent example of a language that has both nominative and structural types, is Scala (It's called a refinement in this section.) Go's interfaces are also an example of structural typing. In Scala, you can declare a type to be a (structural) refinement of another (nominative) type, i.e. you can add further (or in the case you are refining the top type only) restrictions on top of what the nominative type prescribes. The most common use case is to leave out the nominative type from the declaration in which case it is implicitly the top type. Structural refinements mirror the syntax of the corresponding declarations exactly.
This declares a method This means that I can pass an instance of this class:
In your case, it would look something like this:
I.e. we have declared Drawing from the same idea as Scala, re-using the declaration syntax as the type syntax, it might look something like this in C#:
Note that in the current version of Scala on the JVM, in some cases, calling such methods is indeed implemented using reflection and thus has a rather high performance cost. Even the spec, which tries to steer clear of implementation issues, has this footnote:
But, that's a question of implementation, not language design, that was driven by constraints of the target language of the compiler, in particular the fact that such types cannot be represented in the JVM's type system. Maybe, at some point, the implementation can be switched over to use The situation is different in case of C#:
Now that I think about it: C# actually has a limited form of structural types in the form of anonymous types. A natural evolution in terms of already existing language semantics would be to allow not only instantiating but also declaring anonymous types. Currently, you can only instantiate an anonymous type with a syntax that resembles an object initializer with the class name missing. A natural extension of this syntax toward structural types would be to
|
@JoergWMittag: "Two types are compatible if they have the same structure." However, in my case, I would prefer the following syntax than the one you proposed:
Maybe both could be better depending if the Visit method is interesting for SyntaxNode that does not have the OperatorToken property or not. |
This is "structural interfaces". See also #154. |
If you think about Roslyn SyntaxNode (as an example), we have 9 types of SyntaxNode with the OperatorToken property without any common type.
Instead of writing a visitor that have 9 specific Visit (VisitBinaryExpression, VisitPrefixUnaryExpression, VisitPostfixUnaryExpression, etc) to be able to use the OperatorToken, I would love to be able to write something like this:
Then in my visitor, I could just use the Visit:
template is maybe not the best name but I think you got the idea
The text was updated successfully, but these errors were encountered: