编写我的fmap时遇到问题

时间:2009-12-15 22:29:06

标签: haskell functor

我正在尝试为此类型编写一个fmap

data Triangle a  = Triangle {t0 :: Point a, t1 ::  Point a, t2 ::  Point a}

其中Point定义为

data Point a = Point {px :: a, py :: a, pz :: a}

我的实例def是

instance Functor Triangle where 
    fmap f (Triangle v0 v1 v2) = Triangle (f v0) (f v1) (f v2)

我收到以下恭维错误,我无法弄清楚原因

 C:\Scripts\Haskell\Geometry.hs:88:1:
     Occurs check: cannot construct the infinite type: a = Point a
     When generalising the type(s) for `fmap'
     In the instance declaration for `Functor Triangle'

有什么想法吗?

3 个答案:

答案 0 :(得分:8)

instance Functor Point where
    fmap f (Point v0 v1 v2) = Point (f v0) (f v1) (f v2)

instance Functor Triangle where
    fmap f (Triangle v0 v1 v2) = Triangle (fmap f v0) (fmap f v1) (fmap f v2)

在Triangle实例中,fa -> b。我们必须先将其转换为Point a -> Point b。然后,我们可以fmap fTriangle a转换为Triangle b。 (如果我理解你的意图,请注意你将f应用于9个对象[编辑:是27]

答案 1 :(得分:8)

上一个答案为您提供了正确的解决方案,但更明确地了解这里发生的事情可能会有所帮助。 fmap的类型是

fmap :: Functor f => (a -> b) -> f a -> f b

因此,instance声明的类型推断过程如下:

  1. fmap f (Triangle v0 v1 v2)中,f必须包含某种类型a -> b,而(Triangle v0 v1 v2)必须包含Triangle a类型。
    • 根据Triangle的定义,v0v1v2必须包含Point a类型。
    • 由于f已应用于v0v1v2,其参数类型a必须为Point a
    • 糟糕,a = Point a不可满足。
  2. 为什么定义Triangle (fmap f v0) (fmap f v1) (fmap f v2)有效? :

    1. fmap f (Triangle v0 v1 v2)中,f必须包含某种类型a -> b,而(Triangle v0 v1 v2)必须包含Triangle a类型。
      • 根据Triangle的定义,v0v1v2必须包含Point a类型。
      • 假设PointFunctor的实例,如上所述,fmap f v0必须包含Point b类型,其中b的结果类型为f }}。同样适用于v1v2
      • 因此Triangle (fmap f v0) (fmap f v1) (fmap f v2)的类型为Triangle b
      • QED。

答案 2 :(得分:2)

顺便说一下,Functor的一个有趣属性是,只有一个可能的实例可以满足Functor laws

更好的是,可以使用derive包自动为您生成此实例:

{-# LANGUAGE TemplateHaskell #-}

import Data.DeriveTH (derive, makeFunctor)

data Point a = Point {px :: a, py :: a, pz :: a}
$(derive makeFunctor ''Point)

data Triangle a  = Triangle {t0 :: Point a, t1 ::  Point a, t2 ::  Point a}
$(derive makeFunctor ''Triangle)

Imho这是一个胜利,因为如果您决定更改Triangle的定义,则会自动为您保留Functor个实例。