1

我是 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 对象。有人可以解释这个嵌套的事情是怎么回事。

4

2 回答 2

3

这确实是冰糕的签名。你可以在他们的官方文档中阅读更多关于 Sorbet 的信息

在这种情况下,签名本身只是描述参数的形状和build_prescription方法的返回类型。

稍微重新格式化以使其更易于注释:

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。这两个名字应该匹配。

有一种方法可以重写此签名,使其更易于阅读/理解

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.nilables 移出一层,这T.any(Integer, T.nilable(String))意味着T.nilable(T.any(Integer, String))nil

于 2021-05-02T17:15:29.043 回答
1

由于您是专门询问语法而不是语义,因此我将回答您有关语法的问题。

您在此处看到的内容称为消息发送。(在 Java 或 C# 等其他编程语言中,它可能被称为方法调用。)更准确地说,它是带有隐式接收器的消息发送。

消息总是发送给特定的接收者(就像现实世界中的消息一样)。消息发送的一般语法如下所示:

foo.bar(baz, quux: 23) {|garple| glorp(garple) }

这里,

  • foo接收者,即接收消息的对象。请注意,foo它当然可以是任意 Ruby 表达式,例如(2 + 3).to_s.
  • bar消息选择器,或者只是消息。它告诉接收者对象要做什么。
  • 消息选择器后面的括号包含参数列表。在这里,我们有一个位置参数,它是表达式baz(可以是局部变量或另一个消息发送,稍后会详细介绍),以及一个关键字参数quux,它是具有值的关键字23。(同样,该值可以是任意 Ruby 表达式。)注意:除了位置参数和关键字参数之外,还有一些其他类型的参数,即splat 参数和可选的显式块参数
  • 在参数列表之后是​​文字块参数。在 Ruby 中发送的每条消息都可以有一个文字块参数……取决于被调用的方法来忽略它、使用它,或者用它做任何它想做的事情。
  • 块是一段轻量级的可执行代码,因此,就像方法一样,它有一个参数列表和一个主体。参数列表由|管道符号分隔——在这种情况下,只有一个名为 的位置参数garple,但它可以具有方法可以具有的所有相同类型的参数,以及块局部变量。当然,主体可以包含任意 Ruby 表达式。

现在,重要的是其中很多元素是可选的:

  • 您可以省略括号:foo.bar(baz, quux: 23)与 相同foo.bar baz, quux: 23,这也意味着foo.bar()与 相同foo.bar
  • 您可以省略显式接收器,在这种情况下,隐式接收器self,即self.foo(bar, baz: 23)与 相同foo(bar, baz: 23),当然也与 相同foo bar, baz: 23
  • 如果将两者放在一起,这意味着 eg与我之前提到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

因此,这只是将消息发送[]Hash通过解引用常量获得的对象命名空间内的常量解引用结果T,并传递两个位置参数。

第一个位置参数是String,它又是一个常量引用。第二个位置参数是表达式T.any( … ),它是对常量的常量引用T,然后将消息发送any到该常量引用的对象,并传递两个位置参数。

第一个参数是表达式T.nilable(String),它取消引用常量T,将消息发送nilable到取消引用常量的结果T,传递单个位置参数,这是取消引用常量的结果String

第二个参数是表达式T::Hash[ … ]……我将在这里停下来,因为这里真的没有什么要解释的了。有常量、消息发送和参数,我们之前已经多次看到所有这些。

因此,总而言之,关于您关于语法的问题:我们在这里看到的语法元素是

  • 消息发送
  • 论据
  • 常数
  • 命名空间解析运算符(实际上并不是一个单独的语法元素,而只是众多运算符之一)
  • 和一个块文字
于 2021-05-02T20:13:38.887 回答