相当于 Ruby 中的 OCaml 变体
Equivalent to OCaml variants in Ruby
有没有办法在 Ruby 中创建类似 OCaml variants 的东西?
我正在尝试做这样的事情:
type status =
| sent of DateTime * Location
| paid of DateTime
| new
但我不知道如何以干净、安全和简洁的方式做到这一点。
我有一个 Order
class 和一个 status
成员。此字段的可能值为 sent
、paid
或 new
。我的 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
具有值 Sent
或 Paid
时才有意义,字段 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
有没有办法在 Ruby 中创建类似 OCaml variants 的东西?
我正在尝试做这样的事情:
type status =
| sent of DateTime * Location
| paid of DateTime
| new
但我不知道如何以干净、安全和简洁的方式做到这一点。
我有一个 Order
class 和一个 status
成员。此字段的可能值为 sent
、paid
或 new
。我的 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
具有值 Sent
或 Paid
时才有意义,字段 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