I am trying to write an fmap开发者_如何学JAVA for this type
data Triangle a = Triangle {t0 :: Point a, t1 :: Point a, t2 :: Point a}
where Point is defined as
data Point a = Point {px :: a, py :: a, pz :: a}
And my instance def is
instance Functor Triangle where
fmap f (Triangle v0 v1 v2) = Triangle (f v0) (f v1) (f v2)
I am getting the following compliation error and I can't figure out why
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'
Any ideas?
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)
In the Triangle instance, f
is a -> b
. We have to convert it to Point a -> Point b
first. Then we can make fmap f
transform Triangle a
to Triangle b
. (Observe you're applying f
to 9 objects, if I understood your intention correctly) [edit: was 27]
The previous answer gives you a correct solution, but it might be helpful to be more explicit about what's going on here. The type of fmap
is
fmap :: Functor f => (a -> b) -> f a -> f b
So the type inference for your instance
declaration proceeds as follows:
- In
fmap f (Triangle v0 v1 v2)
,f
must have some typea -> b
and(Triangle v0 v1 v2)
must have typeTriangle a
.- By the definition of
Triangle
,v0
,v1
, andv2
must have typePoint a
. - Since
f
is applied tov0
,v1
, andv2
, its argument typea
must bePoint a
. - Oops,
a = Point a
is unsatisfiable.
- By the definition of
Why does the definition Triangle (fmap f v0) (fmap f v1) (fmap f v2)
work? :
- In
fmap f (Triangle v0 v1 v2)
,f
must have some typea -> b
and(Triangle v0 v1 v2)
must have typeTriangle a
.- By the definition of
Triangle
,v0
,v1
, andv2
must have typePoint a
. - Assuming
Point
is an instance ofFunctor
, as above,fmap f v0
must have typePoint b
, whereb
is the result type off
. Likewise forv1
andv2
. - Hence
Triangle (fmap f v0) (fmap f v1) (fmap f v2)
has typeTriangle b
. - QED.
- By the definition of
Btw, an interesting property of Functor
is that there is only one possible instance to make that will satisfy the Functor laws.
Better yet, this instance can be automatically produced for you using the derive package:
{-# 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 this is a win partially because if you decide to change the definition of Triangle
, its Functor
instance is maintained automatically for you.
精彩评论