Randomize
Randomize

Reputation: 9103

Interaction between functor, applicative and Monad

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

Upvotes: 1

Views: 212

Answers (2)

luqui
luqui

Reputation: 60523

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.

Upvotes: 4

chi
chi

Reputation: 116174

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

Upvotes: 5

Related Questions