以下Ruby方法的正确RBS描述是什么?
def insert( id:, **options )
# ...
'Hello World'
end假设使用了以下方法:
insert( id: 123, a: 'A', b: 'B' )发布于 2022-06-19 03:50:36
TL;DR
RBS语法并不总是帮助您告诉您,如果某个任意调用,签名可能会接受什么。它只告诉您签名实际上定义了什么,以及潜在的方法返回了什么。因此,尝试根据如何使用签名而不是实际的签名定义来定义RBS结构可能不会产生预期的结果。
使用RBS或TypeProf生成签名
苏格兰皇家银行
由于未将part #insert方法定义为类的一部分,RBS将假定它是对象的一部分。因此,如果将原始帖子的内容放入foo.rb并调用rbs prototype rb foo.rb,则RBS解释的方法签名如下:
class Object
def insert: (id: untyped, **untyped options) -> "Hello World"
end请注意,RBS v2.5.0非常聪明,并且清楚地告诉您,您既没有定义:id关键字的类型,也没有定义**选项关键字参数将接受的值的名称或类型。这是因为您还没有将它们定义为签名本身的一部分。
但是,它还知道该方法返回一个具有已知值的字符串,并告诉您。
TypeProf
TypeProf v0.21.2的工作方式略有不同,对签名的解释也略有不同。只考虑到上面的方法,它将返回:
# Classes
class Object
private
def insert: (id: untyped, **untyped) -> String
end其中的主要区别在于它认为这个方法是私有的,只是告诉您它返回一个字符串,而不是注释它当前返回的特定字符串。但是,如果展开foo.rb以包括方法调用和方法定义(如下所示):
def insert(id:, **options)
'Hello World'
end
insert(id: 123, a: 'A', b: 'B')然后,TypeProf将尝试根据对该方法的实际调用推断这些类型:
# Classes
class Object
private
def insert: (id: Integer, **String) -> String
end这里的主要不同之处在于,TypeProf使用了一个提供的示例,说明如何调用该方法来推断:id和所收集的关键字参数将采取的预期类型。在您发布的示例中,:id接受一个整数,您收集的关键字参数都是String对象,而且该方法仍然返回一个字符串,尽管TypeProf并不像rbs prototype那样假定该字符串的内容。
如果您以多种方式调用您的方法,则rbs prototype不会更改,但TypeProf将根据其各个调用方传递的类型调整rbs签名的推断类型。例如,如果使用不同的参数两次调用#insert方法:
insert id: 123, a: 'A', b: 'B'
insert id: 'foo', a: 1, b: [], c: {}现在,TypeProf认为签名是:
# Classes
class Object
private
def insert: (id: Integer | String, **Array[untyped] | Hash[untyped, untyped] | Integer | String) -> String
endTypeProf现在认为,:id可以使用整数或字符串,而当前收集的关键字选项可以采用数组、哈希和整数值。
两者都是正确的
从语法的角度来看,这三个结果都是有效的RBS。这取决于您作为作者确定哪个工具最能代表您的意图,或者您是否需要调整RBS语法,以更准确地反映您期望的签名。
此外,每个工具都有不同的操作和输出模式,并以不同的方式解释Ruby代码。因此,只要它们都能输出可用RBS语法验证工具验证的有效RBS语法,就不可能真正更好地对待其中一种。
考虑到您的具体示例,我认为rbs prototype rb提供了一个更简单和更准确的结果,但是TypeProf可能提供更多您似乎期望RBS语法提供的东西(特别是一个显式或鸭式签名),而无需手动更改RBS语法文件。
显式签名产生更显式的RBS。
当然,给予更多明确的签名将有助于苏格兰皇家银行和TypeProf。例如,使用更显式的方法定义在下面的代码上以与上面相同的方式运行它们,在语法中提供了更多的专用性:
def insert(id: 1, a: '', b: '')
'Hello World'
end
insert id: 123, a: 'A', b: 'B'# rbs prototype rb foo.rb
class Object
def insert: (?id: ::Integer, ?a: ::String, ?b: ::String) -> "Hello World"
end
# typeprof foo.rb
class Object
private
def insert: (?id: Integer, ?a: String, ?b: String) -> String
end因为您的方法签名现在更显式,所以rbs prototype和typeprof现在都返回大致相同的结果。您的签名也被更清楚地记录下来,因为您明确地定义了所期望的关键字参数,而不是将其留给一个未知关键字参数的散列集合。
目前,苏格兰皇家银行的语法并不意味着取代像@param这样的码标签。如果您想要那种级别的文档,而不需要强制执行,请使用庭院。如果您想要强制执行签名契约或非鸭子类型,那么您需要使用RBS语法来验证您的代码,并在Ruby生态系统中使用陡峭、Sorbet或其他工具来执行类型。Matz说Ruby不会在内部成为类型化语言,因此在需要时依赖外部工具来执行类型。
另请参阅
https://stackoverflow.com/questions/72634634
复制相似问题