class - Haskell中的异构多态性(正确方法)

标签 class haskell types polymorphism heterogeneous

让一个模块抽象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 ShapeShape构造函数(在 [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/

相关文章:

python - Thrift 服务器对于简单操作来说确实很慢

Bash 原始类型

java - 链接到开始按钮的类不会运行

c# - protected 构造函数派生类问题

arrays - ST monad 中使用数组卡住导致的类型变量不明确

haskell - 如何在 WAI (Warp) 应用程序中执行 IO

java - Java 中的 Float 类型转换

c# - 动态设置泛型类型参数

c# - 如何使类(class)列表可公开访问?

python - 理解 Python 类和对象