Reputation: 9103
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
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
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