4
votes

I am new to Haskell. I've made a type Maybe3.

data Maybe3 a= Just3 a| Unknown3 | Missing3 deriving (Show, Eq, Ord)
eq3 :: Eq a => Maybe3 a-> Maybe3 a-> Bool3
eq3 Unknown3 _ = Unk3
eq3 Missing3 _ = False3
eq3 _ Missing3 = False3
eq3 _ Unknown3 = Unk3 
eq3 (Just3 a) (Just3 b)=if a==b then True3 else False3

How to make Maybe3 an applicative functor? And how to make it a Monad?

1
Your best bet would be to read how Maybe is an Applicative functor and a Monad, and generalise them to your Maybe3 type. - AndrewC
Observe that Maybe3 a is isomorphic to Either Bool a, perhaps that can guide you a bit. Otherwise, how about showing something you have tried, so as to see where you're experiencing problems? At least the Functor instance is really simple, as usual. - leftaroundabout
@PhilipJF By the way, the "You need a monoid structure on Bool" reasoning popped straight in my head because I read the fantastic Comonad Reader article, Abstracting with Applicatives which felt like coming across monad transformers for the first time, but for applicatives, (therefore nicer and less full of <-). I feel it's an article worth advertising, so have taken this opportunity. - AndrewC
Proof that it is only a monad in one way: Left x >>= return = Left x and therefore Left x >>= f = Left x since you can't pattern match on functions. Thus the only way it could be anything other than the usual instance for Either a is if return gave you Left foo for some foo. This though is impossible because of the other identity law: return x >>= \y -> Right y = Right x - Philip JF
@AndrewC to get an applicative with Either only requires a semi-group and not a monoid. This is because pure does not have to use the identity. Thus you have other instances, such as the one generated by const. I think you might have even more instances than the obvious ones using this semi-group construction. - Philip JF

1 Answers

6
votes

Key idea

My understanding is that Missing3 and Unknown3 work a bit like Nothing, except they give a little more feedback about why there's no answer, so might behave slightly differently to each other. Certainly I think Missing3 should behave like Nothing.

Let's look at how these are defined for Maybe:

Functor

Here's the Functor instance for Maybe:

instance  Functor Maybe  where
    fmap _ Nothing       = Nothing
    fmap f (Just a)      = Just (f a)

I think it's clear how to deal with Missing3 and Unknown3 here.

Monad

instance  Monad Maybe  where
    (Just x) >>= k      = k x
    Nothing  >>= _      = Nothing

    (Just _) >>  k      = k
    Nothing  >>  _      = Nothing

    return              = Just
    fail _              = Nothing

You can't help but do the same here with >>= for Missing3 and Unknown3, since you don't have a value to bind with. The only question is do you fail with Unknown3 or Missing3?

Applicative

Here's where there's a little more digging:

instance Applicative Maybe where
    pure = return
    (<*>) = ap

ap                :: (Monad m) => m (a -> b) -> m a -> m b
ap                =  liftM2 id

liftM2  :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 f m1 m2          = do { x1 <- m1; x2 <- m2; return (f x1 x2) }

Now that translates to

mf <*> mx = do
    f <- mf
    x <- mx
    return (f x)

Which you can use all the time to turn a Monad into an Applicative.

Aside: Applicative is great.

In fact, whenever you find yourself writing

this thing = do
    something <- some monadic thing
    more <- some other thing
    yetmore <- another thing too
    return (combine something more yetmore)

you should rewrite it using applicative notation:

this thing = combine <$> some monadic thing 
                     <*> some other thing 
                     <*> another thing too