[ruby]高效使用Type

127 阅读2分钟

强动态类型

Ruby使用强动态类型。为了更好的理解这个概念,看例子:

text = "hello world" + 10

运行上面的语句,Ruby解释器会抛出如下错误:

no implicit conversion of Integer into String (TypeError)

可以看到,Ruby中的type设置还不错,不会像Javascript中可以让不同类型的变量混在一起,很容易造成意想不到的结果。

惯例

在使用ROR(Ruby on Rails)框架的时候,遵循惯“例优于配置”的原则(convention over configuraiton)。也就是,大家在编写程序的时候,会遵循Ruby/Rails社区内一些不成文的惯例,如:

def even?(number)
  number % 2 == 0
end

上述例子反应了2个惯例:

  1. 方法名末尾有问号(?)表示这个方法会返回boolean。其实可以命名这个方法为is_even,但是Ruby程序员并不会这么命名。
  2. 参数名使用number,隐式的表示这个方法的参数是个数字类型。参数可以是任何名称,不过,按照惯例,最好是有意义的并能够反应参数类型的名字。

Sorbet

动态类型语言的弊端在于,当项目规模变大后,会在类型的理解上花费大量的时间。举例子:

class PaymentService
  def process(order)
    ...  
  return receipt
  end
end

上述代码看似直观,但是对于新加入团队的人来说,会问出如下问题:

  1. order的类型是什么?
  2. receipt类型是什么?
  3. 如何知道process方法是返回结果的?

尤其当项目中包含了2种类型的order的时候,Customer::OrderHistory::Order,究竟应该给process方法传递哪个类型?

Stripe的Dev productivity team推出Sorbet - 面向Ruby的静态类型检查工具。

# typed: true
class PaymentService
  extend T::Sig
  sig {params(order: Customer::Order).returns(History::Receipt)}
  def process(order)
    ...  
  return receipt
  end
end

上述代码在原有基础上,引入了Sorbet。通过sig 那一行,我们可以明确的知道process方法返回History::Receipt类型的结果,同时,它需要Customer::Order类型的参数。

可以查看Stripe的slides、video了解Stripe内部对Sorbet的反馈(总体是正向的)

Reference

How to work effectively with types in Ruby