Reputation: 1487
I am trying to write an fmap 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?
Upvotes: 2
Views: 347
Reputation: 24774
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.
Upvotes: 2
Reputation: 55989
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:
fmap f (Triangle v0 v1 v2)
, f
must have some type a -> b
and (Triangle v0 v1 v2)
must have type Triangle a
.
Triangle
, v0
, v1
, and v2
must have type Point a
.f
is applied to v0
, v1
, and v2
, its argument type a
must be Point a
.a = Point a
is unsatisfiable.Why does the definition Triangle (fmap f v0) (fmap f v1) (fmap f v2)
work? :
fmap f (Triangle v0 v1 v2)
, f
must have some type a -> b
and (Triangle v0 v1 v2)
must have type Triangle a
.
Triangle
, v0
, v1
, and v2
must have type Point a
.Point
is an instance of Functor
, as above, fmap f v0
must have type Point b
, where b
is the result type of f
. Likewise for v1
and v2
.Triangle (fmap f v0) (fmap f v1) (fmap f v2)
has type Triangle b
.Upvotes: 8
Reputation: 25654
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]
Upvotes: 8