如何解决在 Purescript 中使用 ADT 的构造函数作为类型
以下是来自 PureScript by Example
的 ADT 示例data Shape
= Circle Point Number
| Rectangle Point Number Number
| Line Point Point
| Text Point String
type Point =
{ x :: Number,y :: Number
}
想做这样的事情有意义吗?
boundingCircle :: Shape -> Circle
boundingBox :: Shape -> Rectangle
目前,这是不允许的,因为 Circle 不是一种类型。我也许可以让 Shape 成为一个类型类,而 Circle、Rectangle 等都有 Shape 的实例。然后我失去了 Shape 是正好 4 个构造函数的并集的语义(模式匹配变得更加混乱)。
将 ADT 构造函数视为类型本身真的值得避免吗?
解决方法
将 ADP 构造函数视为类型是否值得避免是一个毫无意义的问题,因为那是不可能的。 ADT 构造函数不能是类型,这就是结束。
但是,如果您想让 Circle
和 Rectangle
作为它们自己的类型可用,但仍然保留 Shape
作为 ADT,那么没有什么不可能:
type Circle = { center :: Point,radius :: Number }
type Rectangle = { center :: Point,width :: Number,height :: Number }
data Shape
= Circle Circle
| Rectangle Rectangle
| Line Point Point
| Text Point String
boundingCircle :: Shape -> Circle
boundingBox :: Shape -> Rectangle
,
我也许可以让 Shape 成为一个类型类,而 Circle、Rectangle 等 有形状的实例。然后我失去了 Shape 是 恰好 4 个构造函数的并集(模式匹配变得更加混乱)。
实际上您可以同时拥有 4 种独立的类型和简单的模式匹配。
首先是原始示例中与构造函数对应的各个类型:
data Circle = Circle Point Number
data Rectangle = Rectangle Point Number Number
data Line = Line Point Point
data Text = Text Point String
然后 Shape
类型可以是 4 个单独类型的简单“联合”(或“和”):
data Shape = CircleShape Circle | RectangleShape Rectangle | LineShape Line | TextShape Text
这个设置可以让你做你想做的两件事。您提到的函数的形式大致如下:
boundingCircle :: Shape -> Circle
boundingCircle (CircleShape (Circle center radius)) = Circle (...) (...)
boundingCircle (CircleRectangle (Rectangle corner length width)) = Circle (...) (...)
对其余情况依此类推,这也表明您可以对 Shape
进行模式匹配,并知道它是四种特定变体之一。
虽然这不是“免费的胜利”。一切都有利有弊,决定如何设计数据类型并不总是那么简单。特别是,通过上述内容,您会注意到 Shape
上的模式匹配虽然仍然可能,但已经变得更加冗长。很多时候,您实际上并不希望将 Circle
之类的东西作为它们自己的类型。或者您可能对 typeclass 方法感到满意 - 虽然这会阻止您进行模式匹配,但您仍然可以将 boundingCircle
等作为该 typeclass 的方法,或者作为可以轻松从 typeclass 方法派生的其他函数.
确实,我建议这里我最喜欢的方法可能正是 typeclass 之一 - 如果您正在设计 typeclass,则可以准确地放入所需的方法,仅此而已。但在这些情况下,从来没有任何“正确答案”——你权衡利弊,做最适合你的用途。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。