-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Generic Specializations #3298
Comments
I am 👍 on type specialization. The two questions or aspect to address IMO are:
|
Yeah! Using the "most specific match" resolution and constraints just as for method parameter restrictions (shouldn't they be called "constraints" also?). As mentioned in #934 - preferably allowing both unions and even intersections (also for method param restriction then). Preferably the same logic should be refactored and re-used if possible for consistency in the compiler.
I see no reason for artificially limiting extension. If Type(ElementT) is used somewhere and Type(Bool) specific method is used, compiler will complain just as now. |
I'm not 100% sure if my use-case fits into this issue or should be it's own, but... I was hoping to do something like this (using the pseudo-syntax in this thread): class Hash(K, V = Array(K))
# ...
end This is a little bit more advanced that proposed above, I believe, as it places restrictions on the relationships between generics. The use-case in my case is for topological sorting of certain enumerable types. For instance, if we wanted to sort entries in a |
@ozra do you want me to repost the above use case on that issue then? |
@bjeanes - that's probably good, to make it easier for new-comers and old-timers alike to track it? |
I'm sure that this particular issue can be solved with access to |
This issue is just a fancier syntax for things we can already do: https://play.crystal-lang.org/#/r/6hs2 I think this issue can be closed. Having specializations is confusing:
|
I wouldn't necessarily rule this out completely. We need to revisit the generics system anyway, and should keep such ideas in mind. |
Having something like the proposed syntax enables us to specialize the implementation in a different part of the code base. That's a bonus for me. |
@bcardiff it looks close to #934 (comment) even though the semantic is different.. For this issue I'd suggest I think we need to keep the 2 issue' solutions compatible |
class Foo(MyT = Symbol)
def do_stuff() p "Do something with symbol-specific" end
end This syntax made me think that the type of class Foo(MyT = Bool)
def type
MyT
end
end
Foo.new.type # => Bool
Foo(String).new.type # => String Following the standard restriction would be better imo if possible. i.e EDIT: Having default generic types would be 💯 |
That makes no sense for me. How would you imagine an Enumerable with default generic type? 🤔 |
Mmm I'd have to think about what I wanted it for, but IIRC it was for that specify shard I was working on. Where, if you had defaults, could do like |
@Blacksmoke16 |
@straight-shoota Isn't that what |
@vladfaust default generic types can be very useful, for example we could have class Hash(K, V, HasherT = Crystal::Hasher)
end So that is most cases when you create a Hash, the hasher will be the default one from crystal |
But keep in mind that this issue is not about generic default & restrictions (which is covered by #934) but about generic specialization. If you don't see the difference, ask on the chat or the forum. |
As @bcardiff mentions, being able to add specializations as monkey patches is a great plus, and almost likely scenario (implementing a library with SomeSuperFastThing and wanting to specialize Set specifically for that, etc.) As @Blacksmoke16 & @bew mentions, defaults feels lika a given; basically: anything that can be done for run-time arguments in a method call should translate and be mirrored in appropriate syntax for generic parametrizations, including named and positional params and all — that would be the cleanest approach afaic. |
What about something like this? class Foo(T) where T == Bool || T < Int
end The condition after the class MyContainer(T, N) where ((N != 0) && ((N & (~N + 1)) == N))
def optimized_method
...
end
end The macro interpreter has no trouble evaluating something like that. I think this is the easiest to implement and most versatile solution. And in the API docs, we could add a section named something like "generic specializations" and put the Edit: I admit the example above was bad. You could just add a check to the method body, of course. Where this would be more useful is when you want to extend a type. A better example would be: class Matrix(T, N, M)
# operations that make sense for any matrix
end
class Matrix(T, N, M) where N == M
# some operations only make sense for square matrices
def determinant
# ...
end
def inverse
# ...
end
end |
The "as generic as runtime code, but with compile time values (here counting types as values)" really is the most powerful, and consistent. Compare |
I realized, even though its been mentioned many times in comments on other issues, and it was touched upon in #934, that I couldn't find an issue specifically targeting specializations. Here goes:
Shitload of Effort
I do realize the generics are not completely stable yet, and this would be a further amount of blood, sweat and tears.
It would be very nice to be able to do specializations
I'll try to leave generic parametrization constraints out of this issue, so #934 can stand for that, however for clarity an "exact match only" "constraint" syntax could be used here (
T = SomeT
).I use typeparam names according to #3294.
Use Cases
Use-case is of course for performance reasons in containers and low-level readers, writers etc. For instance Set(T) now uses Hash(T) behind the scenes, but Set(Symbol), Set(Int32), Set(Char), etc. could use much more efficient implementations, without the user having to care much about it, same goes for other containers, etc. Win win.
The text was updated successfully, but these errors were encountered: