3

The haskell book wants me to implement the traversable instance for

newtype Constant a b = Constant { getConstant :: a }

including all necessary superclasses. The code below passes Quickcheck/Checkers, but acts funny

import Test.QuickCheck
import Test.QuickCheck.Checkers
import Test.QuickCheck.Classes

newtype Constant a b = Constant { getConstant :: a }

instance Functor (Constant a) where
  fmap f (Constant a) = Constant a

instance Foldable (Constant a) where
  foldr f z (Constant x) = z

instance Traversable (Constant a) where
  traverse f (Constant a) = pure $ Constant a    

type TI = []
main = do
  let trigger = undefined :: TI (Int, Int, [Int])
  quickBatch (traversable trigger)

When I try to use the traversable instance like so:

traverse (\x -> [x + 1]) $ Constant 5 

I do not get Constant [5] which I was hoping for, but rather

traverse (\x -> [x + 1]) $ Constant 5
  :: (Num b, Num a) => [Constant a b]

What does it mean? Have I done something wrong?

The Unfun Cat
  • 29,987
  • 31
  • 114
  • 156

1 Answers1

5

When I try to use the traversable instance like so:

traverse (\x -> [x + 1]) $ Constant 5 

I do not get Constant [5] which I was hoping for [...]

You're not going to get Constant [5]. Let's write the type for traverse:

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

...and line it up with your instantiation of it:

                  -- I've substituted `x` for `a` and `y` for `b` in the
                  -- first type, because otherwise my head hurts.
                  (x -> f  y) -> t            x -> f  (t            y)
(Num a, Num b) => (a -> [] a) -> (Constant b) a -> [] ((Constant b) a)

So we have:

t = Constant b
f = []
x = Num a => a
y = NUm b => b

Note that the type for traverse implies that t will be the same in the argument and the result. Since you're using Constant 5 :: Num a => Constant a b as the argument, that implies that you can never have Constant [5] :: Num a => Constant [a] b in the result, because Constant a /= Constant [a].

Community
  • 1
  • 1
Luis Casillas
  • 29,802
  • 7
  • 49
  • 102
  • Thanks, will think about it. Is my implementation correct? – The Unfun Cat May 26 '16 at 18:39
  • Still, it does not return `Constant 5` or anything concrete either, it just returns a type with a constraint or something similar... – The Unfun Cat May 26 '16 at 18:49
  • 3
    @TheUnfunCat: Your implementation looks correct. One exercise you could try to appreciate this is this: how would you write a different `Traversable` instance for `Constant`? (Hint: it's a trick question!) Anyway, your example should evaluate to the value `[Constant 5]`; you should be able to pattern match against the result to verify that. That's the point of the `Constant` functor—it's a limit case for `Functor`/`Applicative`/`Traversable` that just ignores the functions you're mapping over it. – Luis Casillas May 26 '16 at 18:59