让一个模块抽象Area
操作(错误定义)
class Area someShapeType where
area :: someShapeType -> Float
-- module utilities
sumAreas :: Area someShapeType => [someShapeType]
sumAreas = sum . map area
让一个 后验 显式形状类型模块(良好或可接受的定义)
data Point = Point Float Float
data Circle = Circle Point Float
instance Surface Circle where
surface (Circle _ r) = 2 * pi * r
data Rectangle = Rectangle Point Point
instance Surface Rectangle where
surface (Rectangle (Point x1 y1) (Point x2 y2)) = abs $ (x2 - x1) * (y2 - y1)
放一些数据
c1 = Circle (Point 0 0) 1
r1 = Rectangle (Point 0 0) (Point 1 1)
然后,尝试使用
totalArea = sumAreas [c1, r1]
[c1, r1]
类型必须扩展为 [Circle]
或 [Rectangle]
! (并且无效)我可以使用
forall
和 一个额外的data
像这样输入data Shape = forall a . Surface a => Shape a
sumSurfaces :: [Shape] -> Float
sumSurfaces = sum . map (\(Shape x) -> surface x)
然后,下一个代码成功运行
sumSurfaces [Shape c1, Shape r1]
但我认为,使用
data Shape
和 Shape
构造函数(在 [Shape c1, ...]
和 lambda 参数上)是丑陋的(我的第一个 [也是不好的] 方式很漂亮)。做“Haskell中的异构多态性”的正确方法是什么?
非常感谢您的宝贵时间!
最佳答案
你的第一个(也是坏的)方式并不漂亮,它是 Lispy。这在静态类型语言中是不可能的。即使你在例如做这样的事情Java 你实际上是通过使用基类指针来引入一个单独的量化步骤,这类似于 data Shape = forall a. Surface a
.
关于存在量化是否好存在争议,我认为大多数 Haskellers 不太喜欢它。在这里使用肯定不是正确的:sum [ area c1, area c2 ]
更容易并且效果也很好。但是肯定有更复杂的问题看起来不同。当您“需要”异构多态性时,存在主义就是要走的路。
请记住,您总是可以解决这个问题:由于 Haskell 是惰性的,您可以“先发制人地”应用所有可能的操作(在这个例子中它只是 area
),将所有结果存储在某个记录中,并输出一个列表这些记录而不是多态对象的列表。这样您就可以保留所有信息。
或者,更惯用的是,根本不生成此类对象的列表。你想对对象做点什么,那么为什么不把这些 Action 传递给你产生不同Shape
的函数呢? s,并将它们应用到位!这种逆转将存在量化换成了普遍量化,后者被更广泛地接受。
关于class - Haskell中的异构多态性(正确方法),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13672482/