我可以在方法签名中指定鸭子类型吗?
Can I specify a duck type in method signatures?
示例代码如下:
# typed: true
class KeyGetter
sig {params(env_var_name: String).returns(KeyGetter)}
def self.from_env_var(env_var_name)
return Null.new if env_var_name.nil?
return new(env_var_name)
end
def initialize(env_var_name)
@env_var_name = env_var_name
end
def to_key
"key from #{@env_var_name}"
end
def to_s
"str from #{@env_var_name}"
end
class Null
def to_key; end
def to_s; end
end
end
运行 srb tc
失败
key_getter.rb:7: Returning value that does not conform to method result type https://srb.help/7005
7 | return Null.new if env_var_name.nil?
^^^^^^^^^^^^^^^
Expected KeyGetter
key_getter.rb:6: Method from_env_var has return type KeyGetter
6 | def self.from_env_var(env_var_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Got KeyGetter::Null originating from:
key_getter.rb:7:
7 | return Null.new if env_var_name.nil?
^^^^^^^^
我看到了几种解决此问题的方法:
- 在签名中使用类似
.returns(T.any(KeyGetter, KeyGetter::Null))
的内容。
- 使
KeyGetter::Null
继承自 KeyGetter
。
提取一个 "interface" 并期待它。
class KeyGetter
module Interface
def to_key; end
def to_s; end
end
class Null
include KeyGetter::Interface
end
include Interface
sig {params(env_var_name: String).returns(KeyGetter::Interface)}
def self.from_env_var(env_var_name)
return Null.new if env_var_name.nil?
return new(env_var_name)
end
但我想知道(但未在文档中找到)的是:我可以描述鸭子类型吗?就像我们可以在 YARD 中做的那样,例如:
# @returns [(#to_s, #to_key)]
或者这是一个天生有缺陷的想法(因为理想情况下我们也需要注释 duck 类型的方法。这样做时不要迷失在语法中)。
所以是的,我们可以在这里注释鸭子类型内联吗?如果不是,我们应该怎么做?
But what I'd like to know (and didn't find in the docs) is: can I describe the duck type? Like we can do in YARD, for example:
我发现 sorbet 对具有特定键的散列的支持非常有限(流称为 "sealed object")。您可以尝试这样的操作,但 foo
将被识别为 T::Hash[T.untyped, T.untyped]
,或最多 T::Hash[String, String]
。
extend T::Sig
sig { returns({to_s: String, to_key: String}) }
def foo
T.unsafe(nil)
end
T.reveal_type(foo)
foo.to_s
foo.to_key
他们试图用 Typed Struct ([T::Struct]
) 来解决这个问题,但这与你自己定义 class/interface 没有什么不同。
Sorbet 确实支持元组,但在这里也不理想。 See on Sorbet.run
Or is it an inherently flawed idea (because ideally we need to annotate the duck type's methods too. And not get lost in syntax while doing that).
既然要注解duck类型的方法,那就更需要为它定义一个class。在您概述的方法中,我最喜欢选项 (2)。
您也可以将 NULL 设置为常数值。但是考虑到当前代码的实现方式,它可能不如选项 (2)
KeyGetter::NULL = KeyGetter.new(nil)
示例代码如下:
# typed: true
class KeyGetter
sig {params(env_var_name: String).returns(KeyGetter)}
def self.from_env_var(env_var_name)
return Null.new if env_var_name.nil?
return new(env_var_name)
end
def initialize(env_var_name)
@env_var_name = env_var_name
end
def to_key
"key from #{@env_var_name}"
end
def to_s
"str from #{@env_var_name}"
end
class Null
def to_key; end
def to_s; end
end
end
运行 srb tc
失败
key_getter.rb:7: Returning value that does not conform to method result type https://srb.help/7005
7 | return Null.new if env_var_name.nil?
^^^^^^^^^^^^^^^
Expected KeyGetter
key_getter.rb:6: Method from_env_var has return type KeyGetter
6 | def self.from_env_var(env_var_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Got KeyGetter::Null originating from:
key_getter.rb:7:
7 | return Null.new if env_var_name.nil?
^^^^^^^^
我看到了几种解决此问题的方法:
- 在签名中使用类似
.returns(T.any(KeyGetter, KeyGetter::Null))
的内容。 - 使
KeyGetter::Null
继承自KeyGetter
。 提取一个 "interface" 并期待它。
class KeyGetter module Interface def to_key; end def to_s; end end class Null include KeyGetter::Interface end include Interface sig {params(env_var_name: String).returns(KeyGetter::Interface)} def self.from_env_var(env_var_name) return Null.new if env_var_name.nil? return new(env_var_name) end
但我想知道(但未在文档中找到)的是:我可以描述鸭子类型吗?就像我们可以在 YARD 中做的那样,例如:
# @returns [(#to_s, #to_key)]
或者这是一个天生有缺陷的想法(因为理想情况下我们也需要注释 duck 类型的方法。这样做时不要迷失在语法中)。
所以是的,我们可以在这里注释鸭子类型内联吗?如果不是,我们应该怎么做?
But what I'd like to know (and didn't find in the docs) is: can I describe the duck type? Like we can do in YARD, for example:
我发现 sorbet 对具有特定键的散列的支持非常有限(流称为 "sealed object")。您可以尝试这样的操作,但 foo
将被识别为 T::Hash[T.untyped, T.untyped]
,或最多 T::Hash[String, String]
。
extend T::Sig
sig { returns({to_s: String, to_key: String}) }
def foo
T.unsafe(nil)
end
T.reveal_type(foo)
foo.to_s
foo.to_key
他们试图用 Typed Struct ([T::Struct]
) 来解决这个问题,但这与你自己定义 class/interface 没有什么不同。
Sorbet 确实支持元组,但在这里也不理想。 See on Sorbet.run
Or is it an inherently flawed idea (because ideally we need to annotate the duck type's methods too. And not get lost in syntax while doing that).
既然要注解duck类型的方法,那就更需要为它定义一个class。在您概述的方法中,我最喜欢选项 (2)。
您也可以将 NULL 设置为常数值。但是考虑到当前代码的实现方式,它可能不如选项 (2)
KeyGetter::NULL = KeyGetter.new(nil)