我正在尝试为此类型编写一个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'
有什么想法吗?
答案 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实例中,f
为a -> b
。我们必须先将其转换为Point a -> Point b
。然后,我们可以fmap f
将Triangle a
转换为Triangle b
。 (如果我理解你的意图,请注意你将f
应用于9个对象[编辑:是27]
答案 1 :(得分:8)
上一个答案为您提供了正确的解决方案,但更明确地了解这里发生的事情可能会有所帮助。 fmap
的类型是
fmap :: Functor f => (a -> b) -> f a -> f b
因此,instance
声明的类型推断过程如下:
fmap f (Triangle v0 v1 v2)
中,f
必须包含某种类型a -> b
,而(Triangle v0 v1 v2)
必须包含Triangle a
类型。
Triangle
的定义,v0
,v1
和v2
必须包含Point a
类型。f
已应用于v0
,v1
和v2
,其参数类型a
必须为Point a
。a = Point a
不可满足。为什么定义Triangle (fmap f v0) (fmap f v1) (fmap f v2)
有效? :
fmap f (Triangle v0 v1 v2)
中,f
必须包含某种类型a -> b
,而(Triangle v0 v1 v2)
必须包含Triangle a
类型。
Triangle
的定义,v0
,v1
和v2
必须包含Point a
类型。Point
是Functor
的实例,如上所述,fmap f v0
必须包含Point b
类型,其中b
的结果类型为f
}}。同样适用于v1
和v2
。Triangle (fmap f v0) (fmap f v1) (fmap f v2)
的类型为Triangle b
。答案 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
个实例。