haskell - Haskell 中存在类型的编译问题

标签 haskell existential-type heterogeneous heterogeneous-array

我编写了一个简单的类型类Shape:

class Shape a where
  draw   :: a -> IO ()
  move   :: (Double,Double) -> a -> a
  area   :: a -> Double
  circum :: a -> Double

我还有具体类型 CircleRectTriangle 实例化此类型类,如下所示:

data Circle = Circle Point Double deriving (Show)

instance Shape Circle where
  draw       (Circle centre radius) = putStrLn $ "Circle [" ++ show centre ++ ", " ++ show radius ++ "]"
  move (x,y) (Circle centre radius) = Circle (movePoint x y centre) radius
  area   (Circle _ r) = r ^ 2 * pi
  circum (Circle _ r) = 2 * r * pi

movePoint :: Double -> Double -> Point -> Point
movePoint x y (Point x_a y_a) = Point (x_a + x) (y_a + y)

为了处理包含具体类型CircleRectTriangle实例的异构列表,我遵循了the haskell wiki tutorial on heterogenous collections并实现了一个存在数据类型ShapeType,如下所示:

{-# LANGUAGE ExistentialQuantification #-}

data ShapeType = forall a . Shape a => MkShape a

我让 ShapeType 实例化 Shape 类型类:

instance Shape ShapeType where
  area     (MkShape s) = area s
  circum   (MkShape s) = circum s
  draw     (MkShape s) = draw s
  move (x,y) (MkShape s) =  move (x,y) s  -- this does not compile

现在我可以在代码中使用它,如下所示:

rect = Rect (Point 0 0) (Point 5 4)
circle = Circle (Point 4 5) 4
triangle = Triangle (Point 0 0) (Point 4 0) (Point 4 3)

shapes :: [ShapeType]
shapes = [MkShape rect, MkShape circle, MkShape triangle]

main = do
  print $ map area shapes
  print $ map circum shapes
  mapM_ draw shapes

不幸的是,只有当我省略该行时才会编译

移动 (x,y) (MkShape s) = 移动 (x,y) s

否则我会收到以下编译错误:

error:
    * Couldn't match expected type `ShapeType' with actual type `a'
      `a' is a rigid type variable bound by
        a pattern with constructor:
          MkShape :: forall a. Shape a => a -> ShapeType,
        in an equation for `move'
        at C:\\workspace\FPvsOO\src\Lib.hs:102:15-23
    * In the expression: move (x, y) s
      In an equation for `move': move (x, y) (MkShape s) = move (x, y) s
      In the instance declaration for `Shape ShapeType'
    * Relevant bindings include
        s :: a (bound at C:\\workspace\FPvsOO\src\Lib.hs:102:23)

这对我来说没有意义,因为通过模式匹配“提取” 以便在委托(delegate)调用中使用在其他三种情况下效果很好。

知道这里出了什么问题吗?

更新

通过这个简单的修复,代码现在可以按预期工作:

instance Shape ShapeType where
  area     (MkShape s) = area s
  circum   (MkShape s) = circum s
  draw     (MkShape s) = draw s
  move vec (MkShape s) = MkShape (move vec s)

最佳答案

你错过了一个构造函数。你需要

move v (MkShape s) = MkShape $ move v s

我不太相信你的方法真的是最好的;在这种情况下,存在主义类型往往会把工作搞砸。您至少应该考虑一个普通的旧 sum 类型。存在主义对于某些目的来说是无价的,但在其他情况下它们却是有害的。

关于haskell - Haskell 中存在类型的编译问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65062981/

相关文章:

haskell - 存在类型的类型变量引入

java - 解析异构图

Haskell 与现实世界中的过程式编程

haskell - 在 Haskell 中使用 Data.Mod 进行模幂运算

Haskell Gloss,在动画功能中从控制台读取不会更新绘图

ios - swift 3 : Is there a way to cast an object to a class and protocol at the same time?

performance - 当它们应该是相同的东西时,为什么 >>= 比 concatMap 快?

haskell - 如何定义严格的存在类型?

c++ - 异构列表、虚函数和成员数据

c++ - 创建/管理异构容器(c++)