-
-
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
[RFC] Allow generic instantiations with empty type vars everywhere #10204
Comments
Can you demonstrate a practical use-case? |
The most common scenario of empty splats in generic type arguments is the argument types of class Window; end
class Callback(*Args)
@fn : Proc(Window, *Args, Nil) # requires #8520
# ...
def initialize(@parent : Window, fn : Proc(Window, *Args, _))
@fn = fn
end
def run(*args : *Args)
@fn.call(@parent, *args)
end
def disconnect
# @parent would be needed here
end
end Then it's impossible to use argless functors in some places: class Window
getter on_resize = [] of Callback(Int32, Int32) # okay
getter on_close = [] of Callback() # Error: unexpected token: )
getter on_close = [] of Callback(*typeof(Tuple.new)) # Error: can't infer the type of instance variable '@on_close' of Window
def resize(x : Int32, y : Int32)
# do something
on_activate.each &.run x, y
end
def close
# do something
on_close.each &.run # okay
end
end
wnd = Window.new
cb = Callback.new(wnd, ->(w : Window) { puts "closing" })
# Crystal is able to deduce the generic type arguments for `cb`,
# which are the empty splat, so having `Callback()` is entirely valid here
typeof(cb) # => Callback()
wnd.on_close << cb # okay Not supporting empty type splats would make generics much harder to work with; generics with 0 type arguments are just a natural consequence of the type splat being the only type var of a generic type. Allowing empty parentheses fills in this syntactic gap. |
And using just |
Yes, because omitting the type vars isn't the same as providing 0 type vars. To illustrate more clearly: x = [] of Callback # Error: can't use Callback(*Args) as generic type argument yet, use a more specific type
x = [] of Callback() # should be allowed
x = [] of Callback(*typeof(Tuple.new)) # okay
class Window
getter on_close = [] of Callback # Error: can't infer the type of instance variable '@on_close' of Window
getter on_close = [] of Callback() # should be allowed
# not allowed
getter on_close = [] of Callback(*typeof(Tuple.new))
end Part of the confusion between There is little that can be done on an |
It should be possible to instantiate generic types with 0 type vars everywhere. Currently, this is only doable in contexts where
typeof
is allowed:Below are some contexts where the same splat trick isn't doable:
I propose that we allow a pair of empty parentheses to represent a generic instantiation with 0 arguments: (note that inheritance and module inclusion further require #3649, and instance variables probably need #8520)
I can't think of any syntactic ambiguities that would arise from this. Semantic-wise,
T
andT(...)
already mean different things whenT
is generic, e.g.Tuple
alone in a def type restriction matches all tuples regardless of their type vars, so allowing...
to be empty shouldn't be a huge impact.This does not mean the same as allowing generic definitions to have 0 type vars. The following are still illegal:
Thus generic instantiation with 0 arguments is only possible if the generic has a single splat parameter and no other non-splat parameters. (Not using splats doesn't help because instantiations must still refer to the empty
Tuple
type in that case, likeC(typeof(Tuple.new))
, just without a splat operator.)The text was updated successfully, but these errors were encountered: