Add type arguments support to singleton types#2502
Add type arguments support to singleton types#2502allcre wants to merge 1 commit intoruby:masterfrom
Conversation
|
@soutaro Did |
06d9ce0 to
46c9aa2
Compare
Previously, singleton type arguments could not be supported by RBS. This adds support for them, enabling syntax like `singleton(Array)[String, Integer]`.
46c9aa2 to
91eafe8
Compare
|
@allcre Can you give me some examples why we need that type? I'm assuming |
|
@soutaro we use it in Sorbet to represent the type of the attached class to a singleton. It's useful around factories, here's a simple example: class Box
extend T::Sig
extend T::Generic
E = type_member
sig { params(e: E).void }
def initialize(e)
@e = e
end
sig { returns(E) }
def e
@e
end
end
extend T::Sig
sig { returns(T.class_of(Box)[Box[Integer]]) }
def example
Box
end
x = example
T.reveal_type(x) # => T.class_of(Box)[Box[Integer]]
x.new("str") # error: Expected `Integer` but found `String` for argument `e`More involved examples can be found in the documentation: https://sorbet.org/docs/class-of#tclass_of-applying-type-arguments-to-a-singleton-class-type. |
|
RBS expects us to genericize class methods because it only recognizes type parameters at the instance level. class Set[E]
def self.[]: [E] (*E elements) -> Set[E] # `E` is totally not duplicated
endHonestly, that might be a flawed design: class Set[E]
def self.[]: (*untyped elements) -> instance # `instance` is `Set[untyped]`!
end |
|
Thanks @Morriar, I got the use case and agree that it cannot be written in RBS now. Using an interface would be a workaround, but not sure if it can cover the existing use cases. Let me confirm the semantics: the type What should we do for the other singleton methods? class Box[T]
def initialize: (T) -> void
def self.new_array: [T] (T) -> Box[Array[T]]
end
b = Box #: singleton(Box)[String]
b.new("") # => Box[String]
b.new(1) # => type error
b.new_array(1) #=> ???Looks like it only works for |
How about changing the semantics so that type parameters apply on the class level as well? class Box[T]
def self.new_array: (T) -> Box[Array[T]] # No need to genericize the method separately
def self.[]: (T) -> instance # `instance` was `Box[untyped]`, but now `Box[T]`
end |
|
@ParadoxV5 Yeah, it would make sense. But, how can we associate the type parameter |
|
Allowing |
Yes, when limited to the type of the attached class ( But it can also be used to specify the type of the generic parameters to the singleton class: class BoxFactory
class << self
extend T::Sig
extend T::Generic
Kind = type_member
sig { returns(Box[Kind]) }
def create
Box[Kind].new
end
end
end
class Box
extend T::Generic
Kind = type_member
end
extend T::Sig
sig do
type_parameters(:T)
.params(factory: T.class_of(BoxFactory)[BoxFactory, T.type_parameter(:T)])
.returns(Box[T.type_parameter(:T)])
end
def create_box(factory)
factory.create
endHere's the equivalent RBS inline syntax we want with Sorbet: class BoxFactory
#: [E]
class << self
#: -> Box[E]
def create
Box.new #: Box[E]
end
end
end
#: [E]
class Box
end
#: [T] (singleton(BoxFactory)[BoxFactory, T]) -> Box[T]
def create_box(factory)
factory.create
end |
Add support for parameterized singleton types
This PR adds support for type parameters on singleton types to match the functionality available in Sorbet's
T.class_of(X)[Y]syntax. With this change, RBS now supports the equivalent syntax:singleton(X)[Y].Changes
Examples
Previously, only this was supported:
Now this is also supported:
Questions
Should the
Applicationmodule be included in theClassSingletonclass, similar to how it's included inClassInstanceandInterface? Currently, I've implemented the necessary methods directly.Are there additional methods such as
map_typeandeach_typethat should be added to theClassSingletonclass?