Multiplication of value by two monads "Possible"?

I am currently trying to learn Haskell and am facing an unusual problem with the Maybe monad, which I cannot understand.

As an experiment, I try to take a string, convert each letter to an arbitrary number and multiply / combine them. Here is what I still have:

 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 to run 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 am not 100% sure why this error occurs or what it exactly means, although I suspect that it may be because I try to multiply the two mosaics Maybe together - if I change the definition of test to the following, the program compiles and works 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 below, but that didn't work either.

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

I am not sure how to do this. I'm new to Haskell, so it may just be something really simple that I am missing, or that I completely structured everything, but it makes me sick. What am I doing wrong?

+6
source share
3 answers

You may not have an instance of num, so you cannot multiply them immediately. You need to somehow apply the pure function to the values ​​within the context. This is exactly what functors are applicable for!

Applicative functors live in Control.Applicative:

 import Control.Applicative 

So, you have this function, and you want to apply it to two arguments in context:

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

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

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

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

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

When we specialize, we get the function we need:

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

We apply it, and the conclusion is the number you expect.

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

So, in order to compile your code, you need to change your test function to use <$> and <*> , see if you can understand how to do this.

+9
source

Reite's answer is correct, and I usually recommend processing it, however it seems to me that you do not quite understand how to work with Maybe values; if so, now it makes little sense to consider applicative functors.

The definition of Maybe is basically simple

  data Maybe a = Nothing | Just a 

This basically means that it sounds when you read it in plain English. "The value of type Maybe a is the Nothing value for this type or the value of the form Just a ."

Now you can use pattern matching to work with it, with example lists:

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

Basically, it means "If the value is Nothing , then there is nothing that could be undone, so the result will be Nothing again. If the value is Just xs , we can reverse xs and wrap it with Just again to turn it into Maybe [a] ).

Of course, such write functions for every single function that we ever wanted to use with the Maybe value would be tedious; Thus, a higher order acts for salvation! The observation here is that in maybeReverse we did nothing with reverse , we just applied it to the contained value and wrapped the result of it in Just .

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

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

A further observation we can make is that since functions are values, we can also have values ​​of Maybe functions. In order to do something useful with those that we could deploy them again ... or notice that we are in the same situation as in the last paragraph, and we immediately notice that we don’t care what function is in this 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 $ fa 

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

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

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

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

You really don't need to know what is happening with Functor and Applicative objects (although you should take a look at them at some point: they basically generalize the Maybe functions above to other kinds of "context") - basically, just replace f with Maybe , and you will see that these are basically the same functions that we talked about earlier.

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

+5
source

You are right, the problem is that you are trying to multiply two Maybe values ​​together, but (*) only works in Num cases.

As it turned out, Maybe is an instance of the Applicative class. This means that you can "raise" functions that work with type a for functions that work with Maybe a type.

 import Control.Applicative 

Two features provided by Applicative :

pure :: a -> fa Provides a pure value in a "neutral context". For Maybe it's Just .

(<*>) :: f (a -> b) -> fa -> fb Allows you to apply a "function in context" to two "values ​​in context".

So, suppose we have this pure calculation:

 (*) 2 3 

Here are some similar calculations in the Maybe context:

 Just (*) <*> Just 2 <*> Just 3 -- result is Just 6 pure (*) <*> pure 2 <*> pure 3 -- result is Just 6 -- equivalent to the above pure (*) <*> pure 2 <*> Nothing -- Nothing Nothing <*> pure 2 <*> Just 3 -- Nothing Nothing <*> Nothing <*> Nothing -- Nothing 

If the function or one of the arguments is "missing", we return Nothing .

(<*>) - an explicit application operator when working with applications (instead of using a space, as when working with pure values).

It is instructive to examine what (<*>) does with other Applicative instances, for example [] :

 ghci> [succ,pred] <*> pure 3 [4,2] ghci> [succ,pred] <*> [3] [4,2] ghci> pure succ <*> [2,5] [3,6] ghci> [succ] <*> [2,5] [3,6] ghci> [(+),(*)] <*> pure 2 <*> pure 3 [5,6] ghci> [(+),(*)] <*> [2,1] <*> pure 3 [5,4,6,3] ghci> [(+),(*)] <*> [2,1] <*> [3,7] [5,9,4,8,6,14,3,7] 
+4
source

All Articles