user855443
user855443

Reputation: 2948

functor on data type with two constructors - occurs check

I have a data type

data Pnt2d i v = Pnt2d {_p2id:: i, _v2:: V2 v}

and would like to construct a functor to apply to the second component (V2) as

instance Functor (Pnt2d i) where 
fmap f (Pnt2d i v) = Pnt2d i (f v)

but I get an "occurs check". I understand that I can build Functors only on the second parameter of a multi-parameter data type - which is the case here. What is special and probably the cause of the error is that the second type parameter is a type parameter of the inner (V2) type. It seems related to Functor error when using a data inside a data: "cannot construct the infinite type" I checked the type of the functor when written separately and it seems correct. The answer in Having trouble writing my fmap suggests that two nested functors were necessary - is this the case here?

I do not see, how to construct the functor in this case. I tried with Lenses, but to no avail and would be interested to see what the solution is with/without using Lenses.

src/Uniform/Point2d.hs:119:35: error:
    • Occurs check: cannot construct the infinite type: b ~ V2 b
    • In the second argument of ‘Pnt2d’, namely ‘(f v)’
      In the expression: Pnt2d i (f v)
      In an equation for ‘fmap’: fmap f (Pnt2d i v) = Pnt2d i (f v)
    • Relevant bindings include
        f :: a -> b (bound at src/Uniform/Point2d.hs:119:10)
        fmap :: (a -> b) -> Pnt2d i a -> Pnt2d i b
          (bound at src/Uniform/Point2d.hs:119:5)
    |
119 |     fmap f (Pnt2d i v) = Pnt2d i (f v)
    |                                   ^^^

src/Uniform/Point2d.hs:119:37: error:
    • Occurs check: cannot construct the infinite type: a ~ V2 a
    • In the first argument of ‘f’, namely ‘v’
      In the second argument of ‘Pnt2d’, namely ‘(f v)’
      In the expression: Pnt2d i (f v)
    • Relevant bindings include
        v :: V2 a (bound at src/Uniform/Point2d.hs:119:21)
        f :: a -> b (bound at src/Uniform/Point2d.hs:119:10)
        fmap :: (a -> b) -> Pnt2d i a -> Pnt2d i b
          (bound at src/Uniform/Point2d.hs:119:5)
    |
119 |     fmap f (Pnt2d i v) = Pnt2d i (f v)

Upvotes: 0

Views: 109

Answers (1)

castletheperson
castletheperson

Reputation: 33496

In your instance, f has type a -> b, but you're using it as if it has type V2 a -> V2 b.

You need to lift f using fmap so that it has the type you want.

instance Functor (Pnt2d i) where 
    fmap f (Pnt2d i v) = Pnt2d i (fmap f v)

With lenses, you could do:

instance Functor (Pnt2d i) where 
    fmap f = v2 %~ fmap f

Upvotes: 4

Related Questions