开发者

Having trouble writing my fmap

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:

  1. In fmap f (Triangle v0 v1 v2), f must have some type a -> b and (Triangle v0 v1 v2) must have type Triangle a.
    • By the definition of Triangle, v0, v1, and v2 must have type Point a.
    • Since f is applied to v0, v1, and v2, its argument type a must be Point a.
    • Oops, a = Point a is unsatisfiable.

Why does the definition Triangle (fmap f v0) (fmap f v1) (fmap f v2) work? :

  1. In fmap f (Triangle v0 v1 v2), f must have some type a -> b and (Triangle v0 v1 v2) must have type Triangle a.
    • By the definition of Triangle, v0, v1, and v2 must have type Point a.
    • Assuming 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.
    • Hence Triangle (fmap f v0) (fmap f v1) (fmap f v2) has type Triangle b.
    • QED.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜