-
-
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
Uninstantiated generic types as generic type arguments #12027
Comments
Perhaps obvious, but it's important to mention that
|
I don't think we should let un instantiated generic types as instance variables. It's allowed for include to use T as a generic type restriction (Comparable is a good example of that). Regarding ancestors... I guess uninstantiated generic types make sense In a hierarchy. |
Sorry, I don't follow what you're saying.
IIUC this is just focusing on the leakage of
Uninstantiated, yes, but I don't think a free variable make sense. To me, the intention should be clear: does But perhaps I'm missing a use case. You mention |
By writing module Foo(T)
def foo(other : T)
T
end
end
class Bar1(T1)
include Foo(Bar1)
end
class Bar2(T2)
include Foo(Bar2(T2))
end
class Bar3(T3)
include Foo(self)
end
Bar1(Int32).new.foo(Bar1(Int32).new) # => Bar1(T1)
Bar1(Int32).new.foo(Bar1(Char).new) # => Bar1(T1)
Bar2(Int32).new.foo(Bar2(Int32).new) # => Bar2(Int32)
Bar2(Int32).new.foo(Bar2(Char).new) # Error: no overload matches
Bar3(Int32).new.foo(Bar3(Int32).new) # => Bar3(Int32)
Bar3(Int32).new.foo(Bar3(Char).new) # Error: no overload matches
Note that the following is also technically possible: # `F` must be an uninstantiated generic; in C++ this might look like
# `template <template <typename...> typename F> struct Generic { };`
module Comparable::Generic(F)
abstract def <=>(other : F(*T)) forall T
def <(other : F(*T)) forall T; ...; end
end
class Array(T)
include Comparable::Generic(Array)
# the compiler pretends that `Array` subsumes `Array(*T) forall T`
# during abstract def checking, because `T` is undefined
# while this is indeed true, things get tricky if we use
# `Array(U) forall U` instead
def <=>(other : Array); ...; end
end This approach would formalize the use of uninstantiated generics as higher-kinded type variables. |
The argument type for I believe we could probably reverse the call direction of the comparison in Implementing this triggers a compiler bug and sends it into an endless loop (https://github.com/straight-shoota/crystal/tree/test/array-comparable). 🤷♂️ |
It is! Take a look at this: class Foo
getter x
def initialize(@x : Int32)
end
def <=>(other : Foo)
x <=> other.x
end
end
ary = [Foo.new(3), Foo.new(2), Foo.new(1)]
p ary.sort It just works ©️
This is something that I really like about Crystal: no need to delve into super complex type restrictions. If we do restrict it to |
@asterite I don't get your code example. We're talking about I sympathize with the ability to avoid complexity. |
Ah, you are right. Here's a better example: class Foo
getter x
def initialize(@x : Int32)
end
def <=>(other : Foo)
x <=> other.x
end
end
ary1 = [Foo.new(3), Foo.new(2), Foo.new(1)]
ary2 = [Foo.new(1), Foo.new(2), Foo.new(1)]
p ary1 <=> ary2
That makes total sense if you look at the entire error trace... which is hidden by default 🤦 |
A better answer that doesn't use the facepalm emoji (sorry about that): we could check if each element responds to |
Traits / concepts (a more glorified version of #2549) are another way to specify the correct overload signatures without depending on class Array(T)
def <=>(other : Array(U)) : Int32? forall U requires typeof(self[0] <=> other[0]) <= Int32?
# ...
end
# also okay
def <=>(other : Array) : Int32? requires typeof(self[0] <=> other[0]) <= Int32?
# ...
end
end But this alone doesn't make the original issue go away, because we still need to include |
Normally, all generic type arguments of a generic cannot themselves be uninstantiated generics:
But such generics can be formed nonetheless:
Formal type parameters can be leaked in this way too:
So far we have been using types like
Comparable(Array)
in the standard library without any significant issues. What should we do about these types?The text was updated successfully, but these errors were encountered: