1

Good day/night everyone! I have type:

data FixItem m a = KeepItem|SkipItem|FixItem (m (a -> a))
fixItem f = FixItem $ pure f

and I want to write function mapFix :: (a -> b) -> FixItem m a -> FixItem m b. When I try:

mapFix f SkipItem = SkipItem -- good
mapFix f KeepItem = fixItem f -- error "rigid type"!!!
mapFix f (FixItem mf) = FixItem $ pure (.) <*> (pure f) <*> mf -- too!

So, I get error:

    • Couldn't match type ‘b’ with ‘a’
      ‘b’ is a rigid type variable bound by
        the type signature for:
          mapFix :: forall (m :: * -> *) a b.
                    Applicative m =>
                    (a -> b) -> FixItem m a -> FixItem m b
        at src/test.hs:235:11
      ‘a’ is a rigid type variable bound by
        the type signature for:
          mapFix :: forall (m :: * -> *) a b.
                    Applicative m =>
                    (a -> b) -> FixItem m a -> FixItem m b
        at src/test.hs:235:11
      Expected type: b -> b
        Actual type: a -> b
    • In the first argument of ‘fixItem’, namely ‘f’
      In the expression: fixItem f
      In an equation for ‘mapFix’: mapFix f KeepItem = fixItem f
    • Relevant bindings include
        f :: a -> b (bound at src/test.hs:236:8)
        mapFix :: (a -> b) -> FixItem m a -> FixItem m b
          (bound at src/test.hs:236:1)

How to write mapFix or implement Functor instance for such type (FixItem fixes a to a, not to b, i.e. fix is a -> a, not a -> b)?

RandomB
  • 3,367
  • 19
  • 30

1 Answers1

4

You can't implement Functor type class for your data type. It's because of a -> a inside one of your constructors. When you have functions, you should be more careful. But in short, you have type variable a in contravariant position so you can't implement Functor over this type variable.

Though you can implement Invariant for your data type. Because a in both covariant and contravariant positions, your data type is invariant functor.

Can help you:

Example of Invariant Functor?

What is a contravariant functor?

Some blog post

Shersh
  • 9,019
  • 3
  • 33
  • 61
  • I suspected that Functor is impossible, so is this mean that `mapFix` function is impossible too? Btw, when I changed type to `FixItem m a b = FixItem (m (a -> b))|SkipItem|KeepItem` and try to implement Functor, I get again errors about right type 'a'... – RandomB Jun 17 '17 at 09:53
  • Hmm, I got it: composition of `pure f` and `mf` hides `a -> b` in FixITem, while FixItem supports `a -> a` only. So, mapFix is impossible. But is it possible to implement `FixITem m a b = FixITem (m (a -> b))|...` Functor instance? – RandomB Jun 17 '17 at 10:23
  • Ah, OK, done :) `fmap f (FixItem mf) = FixItem $ pure (.) <*> (pure f) <*> mf`. Thanks! – RandomB Jun 17 '17 at 10:28
  • @Paul-AG Yes, functor is impossible in some theoretical sense. It means that `mapFix` function is impossible as well :( If you change `a -> a` into `a -> b` and have type `FixItem m a b` than you can write `mapFix` which when given `b -> c` function can map `FixItem m a b` to `FixItem m a c` because `b` type variable is in covariant position. – Shersh Jun 17 '17 at 12:23