Reputation: 2481
I have a data type that resembles Blah
below but because of a quirk with the type I cannot automatically derive Functor, Applicative, and Monad. So I must do it manually, but I'm not sure how. I tried to take inspiration from the instances for ((->) a)
, but I can't quite figure out the Monad instance.
newtype Blah a = Blah (String -> a) -- deriving (Functor, Applicative, Monad)
-- this seems right
instance Functor Blah where
fmap f (Blah g) = Blah (f . g)
instance Applicative Blah where
pure = Blah . const
-- This is right, right?
(<*>) (Blah f) (Blah g) = Blah $ \x -> f x (g x)
instance Monad Blah where
return = pure
-- I'm not having any luck here.
(>>=) a b = Blah $ \c -> _
Edit: Someone marked this as a duplicate of another, but I don't see where I would've gotten the answer from that. The newtype wrapper is what made this difficult. I looked up the Monad
instance in base for (->) a
before I wrote this question, but the gymnastics in the answer here are what I needed.
Upvotes: 2
Views: 125
Reputation: 55069
Here’s how you can derive this yourself using typed holes. Starting from your code, renamed a bit:
instance Monad Blah where
return = pure
f >>= g = Blah $ \s -> _
You’ll get a message like this:
Found hole ‘_’ with type: b
Relevant bindings include
s :: String
g :: a -> Blah b
f :: Blah a
So we need to produce a b
, given a String
, a Blah a
, and an a -> Blah b
. Well, we already know how to produce an a
from a Blah a
and a String
, by pattern-matching and applying the function in the Blah
:
Blah f >>= g = Blah $ \s -> let h = f s in _
------ -----------
Now we get:
Found hole ‘_’ with type: b
Relevant bindings include
h :: a
s :: String
g :: a -> Blah b
f :: String -> a
So we have an a
, which we can give to g
to get a Blah b
, and pattern-matching on that gives us a String -> b
:
Blah f >>= g = Blah $ \s -> let Blah h = g (f s) in _
----------------
Now we get:
Found hole ‘_’ with type: b
Relevant bindings include
h :: String -> b
s :: String
g :: a -> Blah b
f :: String -> a
So we need a b
, and we have a String
and a String -> b
. That’s easy:
Blah f >>= g = Blah $ \s -> let Blah h = g (f s) in h s
---
And there you go, a correct implementation, guided by the types. You may also find it clearer if you define a helper function to “run” a Blah
:
newtype Blah a = Blah { runBlah :: String -> a }
-- or:
runBlah :: Blah a -> String -> a
runBlah (Blah f) = f
instance Monad Blah where
f >>= g = Blah $ \s -> runBlah (g (runBlah f s)) s
Upvotes: 1
Reputation: 44644
You can derive
those instances. You just need to turn on the GeneralizedNewtypeDeriving
flag, which enables GHC to simply reuse the instance for the wrapped type.
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Blah a = Blah (String -> a) deriving (Functor, Applicative, Monad)
Upvotes: 2