相当于 Ruby 中的 OCaml 变体

Equivalent to OCaml variants in Ruby

有没有办法在 Ruby 中创建类似 OCaml variants 的东西?

我正在尝试做这样的事情:

type status =
| sent of DateTime * Location
| paid of DateTime
| new

但我不知道如何以干净、安全和简洁的方式做到这一点。

我有一个 Order class 和一个 status 成员。此字段的可能值为 sentpaidnew。我的 class 的某些成员仅针对 status 的某些状态设置和使用。

class Order
    field :status # can be :sent, :paid or :new
    field :paid_at # DateTime
    field :sent_at # DateTime
    filed :sent_to # Symbol
end

它运行良好,但就设计而言,我想知道是否有更好的方法来做到这一点。例如,即使状态不是 :sent,这里的 sent_to 字段也可以访问。这种设计的另一个问题是可读性:期望成员名称,没有任何迹象表明 :sent_to 链接到 :sent 状态,而 send_at 链接到 :paid。在这个例子中,可读性还可以,因为只有几个字段链接到一个状态并且字段名称很明显,但我不确定更大的 classes.

是否仍然如此

在没有原生支持求和类型的语言中,通常的方法是使用带有标签的产品类型以及出现在求和类型中的所有字段。在您的情况下,这将给出:

type tag = Sent | Paid | New
type status = {tag: tag; date : date_time; location: location}

此处字段 date 仅当 tag 具有值 SentPaid 时才有意义,字段 location 仅当 tag 的值为 Sent.

这就是我的做法,也许有人能想出比这更好的办法。这不像在 OCaml 中那样干净、安全、简洁。 Ruby 意味着在其他事情上要坚强,或者至少这是我喜欢对自己说的。 :-)

module Status
  Sent = Struct.new(:date, :location)
  Paid = Struct.new(:date)
  Processing = Class.new
end

status = Status::Sent.new("right now dude!", "location unknown")

case status
when Status::Sent
  puts status.date, status.location
when Status::Paid
  puts status.date
when Status::Processing
  puts "Processing things"
else
  puts "I don't know what is going on"
end

如您所见,您使用鸭子类型而不是静态检查类型,并且用 case 语句替换模式匹配

它是可行的,这是一个由非常优秀的 OCaml 程序员完成的很好的例子,但是在 Python 中(Python 应该足够接近 ruby)

https://github.com/BinaryAnalysisPlatform/bap/blob/master/python/adt.py