试图理解这个 ruby 语法
Trying to understand this ruby syntax
我是 ruby 的新手,很多事情让我感到困惑。我相信这个特定的来自 Sorbet,它是某种类型检查库?不确定。这段特定的代码就在方法声明之前
sig { params(order: T::Hash[
String, T.any(
T.nilable(String), T::Hash[
String, T.any(
Integer, T.nilable(String)
)
]
)
]).returns(Types::Order) }
def self.build_prescription(prescription)
# method implementation
order
对象是来自 REST API 的 json 对象。谁能解释一下这个嵌套是怎么回事。
这确实是一个冰糕签名。您可以在 official documentation
中阅读更多关于 Sorbet 的信息
本例中的签名本身只是描述参数的形状和 build_prescription
方法的 return 类型。
稍微重新格式化以便更容易注释:
sig { # A ruby method added by Sorbet to indicate that the signature is starting.
params( # Start to declare the parameters that the method takes
order: T::Hash[ # First param. In this case, it's a Hash...
String, # ... where they key is a String ...
T.any( # ... and the value can be either: ...
T.nilable(String), ... # a nilable String
T::Hash[ # or a Hash ...
String, # ... where the key is a String...
T.any(Integer, T.nilable(String)) # ... and the value is either an Integer or nilable string String
]
)
])
.returns(Types::Order) # Declaration of what the method returns
}
def self.build_prescription(prescription) # the regular Ruby method declaration
不过请注意,这将无法通过 Sorbet 验证,因为签名将参数声明为 order
,而方法将其声明为 prescription
。这两个名字应该匹配。
有一种方法可以重写此签名,使其更适合 read/understand
sig do
params(
prescription: T::Hash[
String,
T.nilable(
T.any(
String,
T::Hash[String, T.nilable(T.any(Integer, String)]
)
)
])
.returns(Types::Order) # Declaration of what the method returns
}
def self.build_prescription(prescription)
请注意,我将 T.nilable
移出一级,因为 T.any(Integer, T.nilable(String))
的意思与 T.nilable(T.any(Integer, String))
相同,但对于 reader 来说更明显该值可以是 nil
既然你具体问的是语法,而不是语义,我会回答你关于语法的问题。
您在此处看到的内容称为消息发送。 (在 Java 或 C# 等其他编程语言中,它可能被称为 方法调用 。)更准确地说,它是使用 隐式接收器发送的消息.
消息总是发送给特定的接收者(就像现实世界中的消息一样)。消息发送的一般语法如下所示:
foo.bar(baz, quux: 23) {|garple| glorp(garple) }
这里,
foo
是接收者,即接收消息的对象。请注意 foo
当然可以是任意 Ruby 表达式,例如(2 + 3).to_s
.
bar
是 消息选择器 ,或者简称为 消息 。它告诉接收者对象要做什么。
- 消息选择器后的括号包含参数列表。在这里,我们有一个 位置参数 ,它是表达式
baz
(它可以是局部变量或另一条消息发送,稍后会详细介绍),还有一个 关键字参数 是关键字 quux
,值为 23
。 (同样,该值可以是任意 Ruby 表达式。)注意:除了位置参数和关键字参数之外,还有一些其他类型的参数,即 splat arguments 和一个可选的 显式块参数 .
- 参数列表之后是文字块参数。 Ruby 中发送的每条消息都可以有一个文字块参数……这取决于调用的方法来忽略它、使用它或做任何它想做的事情。
- 块是一段轻量级的可执行代码,因此,就像方法一样,它有一个参数列表和一个主体 .参数列表由
|
管道符号分隔 - 在这种情况下,只有一个名为 garple
的位置参数,但它可以具有方法可以具有的所有相同类型的参数,加上 块局部变量。当然,正文可以包含任意 Ruby 表达式。
现在,重要的是其中很多元素都是可选的:
- 你可以省略括号:
foo.bar(baz, quux: 23)
与foo.bar baz, quux: 23
相同,这也意味着foo.bar()
与foo.bar
相同。
- 您可以省略显式接收器,在这种情况下,隐式接收器是
self
,即self.foo(bar, baz: 23)
与[=29=相同],这当然与 foo bar, baz: 23
. 相同
- 如果将两者放在一起,那就意味着例如
self.foo()
与我之前提到的 foo
相同:如果你只是 foo
本身而没有上下文,你实际上不知道它是局部变量还是消息发送。只有当您看到接收者或参数(或两者)时,您才能确定它是消息发送,并且只有当您在同一范围内看到赋值时才能确定它是变量。如果你看不到这些东西,那也可能是。
- 您可以省略不使用的块参数列表,也可以完全省略块。
那么让我们来分析一下您在这里看到的语法。第一层是
sig {
# stuff
}
我们知道这是消息发送而不是局部变量,因为有一个文字块参数,变量不带参数,只有消息发送才带参数。
因此,这是将消息 sig
发送到隐式接收器 self
(在模块定义主体中只是模块本身),并将文字块作为唯一参数传递。
该块没有参数列表,只有一个主体。正文内容为
params(
# stuff
).returns(Types::Order)
同样,我们知道 params
是一条消息发送,因为它需要一个参数。所以,这是将消息 params
发送到隐式接收者 self
(这里仍然是模块本身,因为块词法捕获 self
,尽管这是语言语义的一部分,你严格询问语法)。它还将一个参数传递给消息发送,我们将在稍后查看。
然后我们发送另一条消息。我们怎么知道的?好吧,它需要一个参数并有一个接收者。我们将消息 returns
发送到 params
消息发送返回的对象,将表达式 Types::Order
作为唯一的位置参数传递。
Types::Order
,又是一个常量引用(Types
),命名空间解析运算符 (::
),后跟另一个常量引用 (Order
).
接下来,让我们看一下params
的参数:
params(order: T::Hash[
# stuff
])
这里我们有一个关键字参数order
,它的值是表达式T::Hash[ … ]
。 T::Hash
当然又是常量引用、名称空间解析运算符和另一个常量引用。
那么,什么是[]
?实际上,那只是另一条消息发送。 Ruby 有 语法糖 用于有限的、固定的特殊消息列表。一些例子:
foo.call(bar)
等同于 foo.(bar)
.
foo.+(bar)
等同于 foo + bar
。 (与 *
、**
、/
、-
、<<
、>>
、|
、&
、==
、===
、=~
、!=
、!==
、!~
,还有一些我可能忘记了。)
foo.+@
等同于 +foo
。 (与 -@
类似。)
foo.!
等同于 !foo
。 (和 ~
类似。)
self.`("Hello")
和`Hello`
一样,有点晦涩
foo.[](bar, baz)
等同于 foo[bar, baz]
.
foo.[]=(bar, baz, quux)
等同于 foo[bar, baz] = quux
.
因此,这只是将消息[]
发送到通过取消引用常量T
获得的对象的名称空间中取消引用常量Hash
的结果,并传递两个位置参数。
第一个位置参数是String
,它又是一个常量引用。第二个位置参数是表达式 T.any( … )
,它是常量 T
的常量引用,然后将消息 any
发送到该常量引用的对象,传递两个位置参数。
第一个参数是表达式T.nilable(String)
,它解引用常量T
,发送消息nilable
到解引用常量T
的结果,传递单个位置参数,这是取消引用常量 String
.
的结果
第二个参数是表达式T::Hash[ … ]
……我要到此为止了,因为这里真的没有什么要解释的了。有常量、消息发送和参数,我们之前已经多次看到所有这些。
总而言之,关于您关于语法的问题:我们在这里看到的语法元素是
- 消息发送
- 参数
- 常量
- 名称空间解析运算符(实际上并不是一个单独的语法元素,而只是许多运算符中的一个)
- 和块文字
我是 ruby 的新手,很多事情让我感到困惑。我相信这个特定的来自 Sorbet,它是某种类型检查库?不确定。这段特定的代码就在方法声明之前
sig { params(order: T::Hash[
String, T.any(
T.nilable(String), T::Hash[
String, T.any(
Integer, T.nilable(String)
)
]
)
]).returns(Types::Order) }
def self.build_prescription(prescription)
# method implementation
order
对象是来自 REST API 的 json 对象。谁能解释一下这个嵌套是怎么回事。
这确实是一个冰糕签名。您可以在 official documentation
中阅读更多关于 Sorbet 的信息本例中的签名本身只是描述参数的形状和 build_prescription
方法的 return 类型。
稍微重新格式化以便更容易注释:
sig { # A ruby method added by Sorbet to indicate that the signature is starting.
params( # Start to declare the parameters that the method takes
order: T::Hash[ # First param. In this case, it's a Hash...
String, # ... where they key is a String ...
T.any( # ... and the value can be either: ...
T.nilable(String), ... # a nilable String
T::Hash[ # or a Hash ...
String, # ... where the key is a String...
T.any(Integer, T.nilable(String)) # ... and the value is either an Integer or nilable string String
]
)
])
.returns(Types::Order) # Declaration of what the method returns
}
def self.build_prescription(prescription) # the regular Ruby method declaration
不过请注意,这将无法通过 Sorbet 验证,因为签名将参数声明为 order
,而方法将其声明为 prescription
。这两个名字应该匹配。
有一种方法可以重写此签名,使其更适合 read/understand
sig do
params(
prescription: T::Hash[
String,
T.nilable(
T.any(
String,
T::Hash[String, T.nilable(T.any(Integer, String)]
)
)
])
.returns(Types::Order) # Declaration of what the method returns
}
def self.build_prescription(prescription)
请注意,我将 T.nilable
移出一级,因为 T.any(Integer, T.nilable(String))
的意思与 T.nilable(T.any(Integer, String))
相同,但对于 reader 来说更明显该值可以是 nil
既然你具体问的是语法,而不是语义,我会回答你关于语法的问题。
您在此处看到的内容称为消息发送。 (在 Java 或 C# 等其他编程语言中,它可能被称为 方法调用 。)更准确地说,它是使用 隐式接收器发送的消息.
消息总是发送给特定的接收者(就像现实世界中的消息一样)。消息发送的一般语法如下所示:
foo.bar(baz, quux: 23) {|garple| glorp(garple) }
这里,
foo
是接收者,即接收消息的对象。请注意foo
当然可以是任意 Ruby 表达式,例如(2 + 3).to_s
.bar
是 消息选择器 ,或者简称为 消息 。它告诉接收者对象要做什么。- 消息选择器后的括号包含参数列表。在这里,我们有一个 位置参数 ,它是表达式
baz
(它可以是局部变量或另一条消息发送,稍后会详细介绍),还有一个 关键字参数 是关键字quux
,值为23
。 (同样,该值可以是任意 Ruby 表达式。)注意:除了位置参数和关键字参数之外,还有一些其他类型的参数,即 splat arguments 和一个可选的 显式块参数 . - 参数列表之后是文字块参数。 Ruby 中发送的每条消息都可以有一个文字块参数……这取决于调用的方法来忽略它、使用它或做任何它想做的事情。
- 块是一段轻量级的可执行代码,因此,就像方法一样,它有一个参数列表和一个主体 .参数列表由
|
管道符号分隔 - 在这种情况下,只有一个名为garple
的位置参数,但它可以具有方法可以具有的所有相同类型的参数,加上 块局部变量。当然,正文可以包含任意 Ruby 表达式。
现在,重要的是其中很多元素都是可选的:
- 你可以省略括号:
foo.bar(baz, quux: 23)
与foo.bar baz, quux: 23
相同,这也意味着foo.bar()
与foo.bar
相同。 - 您可以省略显式接收器,在这种情况下,隐式接收器是
self
,即self.foo(bar, baz: 23)
与[=29=相同],这当然与foo bar, baz: 23
. 相同
- 如果将两者放在一起,那就意味着例如
self.foo()
与我之前提到的foo
相同:如果你只是foo
本身而没有上下文,你实际上不知道它是局部变量还是消息发送。只有当您看到接收者或参数(或两者)时,您才能确定它是消息发送,并且只有当您在同一范围内看到赋值时才能确定它是变量。如果你看不到这些东西,那也可能是。 - 您可以省略不使用的块参数列表,也可以完全省略块。
那么让我们来分析一下您在这里看到的语法。第一层是
sig {
# stuff
}
我们知道这是消息发送而不是局部变量,因为有一个文字块参数,变量不带参数,只有消息发送才带参数。
因此,这是将消息 sig
发送到隐式接收器 self
(在模块定义主体中只是模块本身),并将文字块作为唯一参数传递。
该块没有参数列表,只有一个主体。正文内容为
params(
# stuff
).returns(Types::Order)
同样,我们知道 params
是一条消息发送,因为它需要一个参数。所以,这是将消息 params
发送到隐式接收者 self
(这里仍然是模块本身,因为块词法捕获 self
,尽管这是语言语义的一部分,你严格询问语法)。它还将一个参数传递给消息发送,我们将在稍后查看。
然后我们发送另一条消息。我们怎么知道的?好吧,它需要一个参数并有一个接收者。我们将消息 returns
发送到 params
消息发送返回的对象,将表达式 Types::Order
作为唯一的位置参数传递。
Types::Order
,又是一个常量引用(Types
),命名空间解析运算符 (::
),后跟另一个常量引用 (Order
).
接下来,让我们看一下params
的参数:
params(order: T::Hash[
# stuff
])
这里我们有一个关键字参数order
,它的值是表达式T::Hash[ … ]
。 T::Hash
当然又是常量引用、名称空间解析运算符和另一个常量引用。
那么,什么是[]
?实际上,那只是另一条消息发送。 Ruby 有 语法糖 用于有限的、固定的特殊消息列表。一些例子:
foo.call(bar)
等同于foo.(bar)
.foo.+(bar)
等同于foo + bar
。 (与*
、**
、/
、-
、<<
、>>
、|
、&
、==
、===
、=~
、!=
、!==
、!~
,还有一些我可能忘记了。)foo.+@
等同于+foo
。 (与-@
类似。)foo.!
等同于!foo
。 (和~
类似。)self.`("Hello")
和`Hello`
一样,有点晦涩foo.[](bar, baz)
等同于foo[bar, baz]
.foo.[]=(bar, baz, quux)
等同于foo[bar, baz] = quux
.
因此,这只是将消息[]
发送到通过取消引用常量T
获得的对象的名称空间中取消引用常量Hash
的结果,并传递两个位置参数。
第一个位置参数是String
,它又是一个常量引用。第二个位置参数是表达式 T.any( … )
,它是常量 T
的常量引用,然后将消息 any
发送到该常量引用的对象,传递两个位置参数。
第一个参数是表达式T.nilable(String)
,它解引用常量T
,发送消息nilable
到解引用常量T
的结果,传递单个位置参数,这是取消引用常量 String
.
第二个参数是表达式T::Hash[ … ]
……我要到此为止了,因为这里真的没有什么要解释的了。有常量、消息发送和参数,我们之前已经多次看到所有这些。
总而言之,关于您关于语法的问题:我们在这里看到的语法元素是
- 消息发送
- 参数
- 常量
- 名称空间解析运算符(实际上并不是一个单独的语法元素,而只是许多运算符中的一个)
- 和块文字