1

I am totally new to Haskell and I am trying to understand better how functor, applicative and monad work together. Below in my example:

import Control.Monad
import Control.Applicative

data FooBar a = Foo a | Bar a deriving (Show)

myf :: FooBar Int -> FooBar Int
myf (Bar a) = Foo (a * 10)
myf (Foo a) = Bar (a * 10)

instance Functor FooBar where
    fmap func (Foo val) = Bar (func val)
    fmap func (Bar val) = Foo (func val)

instance Applicative FooBar where
    pure = Foo
    (Foo f) <*> (Foo x) = Foo (f x)
    (Foo f) <*> (Bar x) = Foo (f x)
    (Bar f) <*> (Foo x) = Bar (f x)
    (Bar f) <*> (Bar x) = Bar (f x)

instance Monad FooBar where
    return = Foo
    (Foo x) >>= f = f x
    (Bar x) >>= f = f x

main = putStrLn $ show $ Foo (+3) <*> Foo 5 >>= myf

What I am trying to achieve is "piping" the value from a Functor/Applicative via monad's bind but I get an error in the main line:

ghc: No instance for (Num (FooBar Int)) arising from a use of `+'
Possible fix: add an instance declaration for (Num (FooBar Int))
In the first argument of `Foo', namely `(+ 3)'
In the first argument of `(<*>)', namely `Foo (+ 3)'
In the first argument of `(>>=)', namely `Foo (+ 3) <*> Foo 5'

Something similar happens if I replace the Applicative with the Functor like this:

main = putStrLn $ show $ (+3) <$> Foo 5 >>= myf

Is actually possible what I am trying to do or there is a mistake in my definitions?

EDIT This is a cleaner solution:

import Control.Monad
import Control.Applicative

data FooBar a = Foo a | Bar a deriving (Show)

myf :: Int -> FooBar Int
myf (a) = return (a * 10)

instance Functor FooBar where
    fmap func (Foo val) = Foo (func val)
    fmap func (Bar val) = Bar (func val)

instance Applicative FooBar where
    pure = Foo
    (Foo f) <*> something = fmap f something
    (Bar f) <*> something = fmap f something

instance Monad FooBar where
    return = Foo
    (Foo x) >>= f = f x
    (Bar x) >>= f = f x

main = putStrLn $ show $ (+) <$> Bar(19) <*> (Foo 3) >>= myf
2
  • There is a problem with myf, the binding operator (>>=) has type ma -> (a -> mb) -> mb. It injects a value of type 'a' into the monadic context and myf doesn't do that.
    – felipez
    Commented Apr 4, 2015 at 15:31
  • @felipez what you say is correct, indeed if I replace the function with myf :: Int -> FooBar Int myf (a) = Foo (a * 10) it will work but it won't be able to switch between Foo and Bar types.
    – Randomize
    Commented Apr 4, 2015 at 16:05

2 Answers 2

5

The problem is here:

myf :: FooBar Int -> FooBar Int

The above causes trouble when you use

something >>= myf

because it requires something to have type FooBar (FooBar Int). This in turn makes numeric constants to be of type FooBar Int and not Int, and (+) operate on "numbers" of type FooBar Int. This triggers the type error.

Maybe you simply want to use

myf something

instead. In your particular case,

main = putStrLn $ show $ myf $ Foo (+3) <$> Foo 5
4
  • please check my answer above to felipez
    – Randomize
    Commented Apr 4, 2015 at 16:06
  • @Randomize I'm not suggesting to change the type, but how you apply the function. I edited to make it clearer.
    – chi
    Commented Apr 4, 2015 at 16:10
  • what you said it was clear to me. What I am trying to achieve is actually writing down a "myf" that works with monad's bind to get the same result as in your case.
    – Randomize
    Commented Apr 4, 2015 at 16:44
  • @Randomize I see. I think that's impossible though, since to use >>= myf the function should have type Int -> FooInt b, but then it loses access to the constructors Foo and Bar you want to swap in the input.
    – chi
    Commented Apr 4, 2015 at 16:49
4

Since you are trying to understand the relationship between Functor, Applicative, and Monad, you might like to know that your Monad and Applicative instances are incompatible. (<*>) must behave the same way as Control.Monad.ap:

ap :: (Monad m) => m (a -> b) -> m a -> m b
ap mf mx = mf >>= (\f -> mx >>= (\x -> return (f x)))

but you have:

Bar id <*>  Bar 0 = Bar 0
Bar id `ap` Bar 0 = Foo 0

In fact, what is causing this is also causing a monad law to be violated:

m >>= return = m

but you have

Bar 0 >>= return = Foo 0

There is also an applicative law violated caused by the same thing.

You cannot simply throw away the information of whether the value was constructed with Foo or Bar the way you are. Since return = Foo, you need to make sure that Foo behaves "purely" -- i.e. combining with it (in (<*>) or (>>=)) does not change the structure of the other argument. One possible way out of this is to always have Bar "taint" the computation:

-- Since we have a Foo, we need to preserve whatever f does:
Foo x >>= f = f x
Bar x >>= f = case f x of   
                  -- If f x returned a Foo, we need to preserve Bar from the left arg:
                  Foo y -> Bar y
                  -- We can do whatever we like with this clause:
                  Bar y -> Bar y

Then use ap to find out what your Applicative instance should be. Interesting to note, this is now isomorphic to Writer Any.

3
  • 1
    Thing go awry even sooner: the Functor instance fails to satisfy fmap id ≡ id. Commented Apr 5, 2015 at 0:52
  • @leftaroundabout, oh yeah, I didn't even see that!
    – luqui
    Commented Apr 5, 2015 at 1:30
  • yes even the functor itself is wrong and thanks to highlight all the problems - it helped. I put this as the right answer :)
    – Randomize
    Commented Apr 5, 2015 at 9:10

Not the answer you're looking for? Browse other questions tagged or ask your own question.