Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiplying the value within two "Maybe" monads?

Tags:

haskell

maybe

I'm currently in the process of trying to learn Haskell, and ran into an odd issue regarding the Maybe monad which I can't seem to figure out.

As an experiment, I'm currently trying to take a string, convert each letter to an arbitrary number, and multiply/combine them together. Here's what I have so far:

lookupTable :: [(Char, Int)]
lookupTable = [('A', 1), ('B', 4), ('C', -6)]

strToInts :: String -> [Maybe Int]
strToInts = map lookupChar
    where 
        lookupChar :: Char -> Maybe Int
        lookupChar c = lookup c lookupTable

-- Currently fails
test :: (Num n, Ord n) => [Maybe n] -> [Maybe n]
test seq = [ x * y | (x, y) <- zip seq $ tail seq, x < y ]

main :: IO ()
main = do
    putStrLn $ show $ test $ strToInts "ABC"

When I try running this, it returns the following error:

test.hs:13:16:
    Could not deduce (Num (Maybe n)) arising from a use of `*'
    from the context (Num n, Ord n)
      bound by the type signature for
                 test :: (Num n, Ord n) => [Maybe n] -> [Maybe n]
      at test.hs:12:9-48
    Possible fix: add an instance declaration for (Num (Maybe n))
    In the expression: x * y
    In the expression: [x * y | (x, y) <- zip seq $ tail seq]
    In an equation for `test':
        test seq = [x * y | (x, y) <- zip seq $ tail seq]

I'm not 100% sure why this error is occurring, or what it exactly means, though I suspect it might be because I'm trying to multiply two Maybe monads together -- if I change the definition of test to the following, the program compiles and runs fine:

test :: (Num n, Ord n) => [Maybe n] -> [Maybe n]
test seq = [ x | (x, y) <- zip seq $ tail seq, x < y ]

I also tried changing the type declaration to the below, but that didn't work either.

test :: (Num n, Ord n) => [Maybe n] -> [Num (Maybe n)]

I'm not really sure how to go about fixing this error. I'm fairly new to Haskell, so it might just be something really simple that I'm missing, or that I've structured everything completely wrong, but this is stumping me. What am I doing wrong?

like image 545
Michael0x2a Avatar asked Mar 08 '14 10:03

Michael0x2a


People also ask

What is maybe monad?

The Maybe monad represents computations which might "go wrong" by not returning a value.

Is a tuple a monad?

One thing I noticed was that Tuple does not have a Monad instance. Which already extremely heavily restricts what we can make the Monad instance be.

What can you do with monads?

monads are used to address the more general problem of computations (involving state, input/output, backtracking, ...) returning values: they do not solve any input/output-problems directly but rather provide an elegant and flexible abstraction of many solutions to related problems.

What are monads in Haskell?

A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.


2 Answers

Maybe does not have a num instance so you cannot multiply them together directly. You need to somehow apply the pure function to the values inside the context. This is exactly what applicative functors are for!

Applicative functors live in Control.Applicative:

import Control.Applicative

So you have this function and you want to apply it to 2 arguments in a context:

(*) :: Num a => a -> a -> a

You probably learned about fmap, it takes a function and applies it to a value in a context. <$> is an alias for fmap. When we fmap the pure function over the maybe value we get the following result:

(*) <$> Just 5 :: Num a => Maybe (a -> a)

So now we have maybe a function and we need to apply it to maybe a value, this is exactly what the applicative functor does. Its main operator is <*> which has the signature:

(<*>) :: f (a -> b) -> f a -> f b

When we specialize it we get the function we need:

(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b

We apply it and the output is the number you expect.

(*) <$> Just 5 <*> Just 5 :: Num a => Maybe a

So to make your code compile you need to change your test function to use <$> and <*>, see if you can figure out how.

like image 171
Reite Avatar answered Nov 15 '22 00:11

Reite


Reite's answer is correct, and it's generally how I'd normally recommend handling it - however, it seems to me that you don't quite understand how to work with Maybe values; if so there is little sense looking at applicative functors right now.

The definition of Maybe is basically just

 data Maybe a = Nothing | Just a

Which basically means exactly what it sounds like when you read it in plain english "A value of type Maybe a is either the value Nothing for that type, or a value of the form Just a".

Now you can use pattern matching to work with that, with the example of lists:

 maybeReverse :: Maybe [a] -> Maybe [a]
 maybeReverse Nothing = Nothing
 maybeReverse (Just xs) = Just $ reverse xs

Which basically means "If the value is Nothing, then there's nothing to reverse, so the result is Nothing again. If the value is Just xs then we can reverse xs and wrap it with Just again to turn it into a Maybe [a] value).

Of course writing functions like this for every single function we ever want to use with a Maybe value would be tedious; So higher order functions to the rescue! The observation here is that in maybeReverse we didn't do all that much with reverse, we just applied it to the contained value and wrapped the result of that in Just.

So we can write a function called liftToMaybe that does this for us:

 liftToMaybe :: (a->b) -> Maybe a -> Maybe b
 liftToMaybe f Nothing = Nothing
 liftToMaybe f (Just a) = Just $ f a

A further observation we can make is that because functions are values, we can also have Maybe values of functions. To do anything useful with those we could again unwrap them... or notice we're in the same situation as in the last paragraph, and immediately notice that we don't really care what function exactly is in that Maybe value and just write the abstraction directly:

 maybeApply :: Maybe (a->b) -> Maybe a -> Maybe b
 maybeApply Nothing _ = Nothing
 maybeApply _ Nothing = Nothing
 maybeApply (Just f) (Just a) = Just $ f a

Which, using our liftToMaybe function above, we can simplify a bit:

 maybeApply :: Maybe (a->b) -> Maybe a -> Maybe b
 maybeApply Nothing _ = Nothing
 maybeApply (Just f) x = liftToMaybe f x

The <$> and <*> operators in Reite's answer are basically just infix names for liftToMaybe (which is also known as fmap) and maybeApply respectively; They have the types

(<$>) :: Functor f => (a->b) -> f a -> f b
(<*>) :: Applicative f => f (a->b) -> f a -> f b

You don't really need to know what the Functor and Applicative things are right now (although you should look into them at some point; They're basically generalizations of the above Maybe functions for other kinds of "context") - basically, just replace f with Maybe and you'll see that these are basically the same functions we've talked about earlier.

Now, I leave applying this to your original problem of multiplication to you (although the other answers kinda spoil it).

like image 23
Cubic Avatar answered Nov 14 '22 23:11

Cubic