1

I do not understand why this sample of code does not work and in RWH book it works:

module Monads where 
    import Data.Maybe
    import Control.Monad
    amap=[("a",1),("bb",2)]
    bmap=[(1,100),(2,200)]
    final=[(100,1000),(200,2000)]

    f::String->IO (Maybe Int)
    f par=do
          a<-lookup par amap
          b<-lookup a bmap
          lookup b final

It does not work and i do not understand how it could work since the last line returns a Maybe something and not a IO something.

I have also tried changing the last line to :
return (lookup b final)
which to my reasoning should be perfect ( lookup returns a Maybe Int and then wrap it with return)

I get the following error in when using return

* Couldn't match type `Maybe' with `IO'
      Expected type: IO Integer
        Actual type: Maybe Integer
    * In a stmt of a 'do' block: b <- lookup a bmap
      In the expression:
        do a <- lookup par amap
           b <- lookup a bmap
           return (lookup b final)
      In an equation for `f':
          f par
            = do a <- lookup par amap
                 b <- lookup a bmap
                 return (lookup b final)
   |
11 |           b<-lookup a bmap
   |              ^^^^^^^^^^^^^
Bercovici Adrian
  • 8,794
  • 17
  • 73
  • 152
  • 2
    The `do` block refers to `Maybe`, not to `IO` here. It looks like the entire thing should be `f par = return $ do ...`, or you could change the type to `f::String->Maybe Int`. – Norrius Mar 01 '19 at 14:07
  • 1
    You're right, this will never work (it would if the return type was `Maybe Int` though). But I don't see that code snippet anywhere in the chapter you linked to. (I actually went through much of RWH not long ago - it's pretty out of date and a lot of the modules they use don't seem to exist any more with the same API - but I don't remember anything like this.) – Robin Zigmond Mar 01 '19 at 14:07
  • It is right before `The list Monad` subchapter. – Bercovici Adrian Mar 01 '19 at 14:11
  • 1
    Nothing in your code suggests that `f` has to return an `IO` value. – chepner Mar 01 '19 at 14:12
  • Wait a second..i thought that when using `do` it is restricted only to the `IO` monad. – Bercovici Adrian Mar 01 '19 at 14:13
  • 2
    No, `do` works for *any* monad. – chepner Mar 01 '19 at 14:14
  • All you really need here, though, is `f :: String -> Maybe Int` with `f par = lookup par amap >>= flip lookup bmap >>= flip lookup final`. IMO, that's clearer than using `do` notation, since it emphasizes the act of chaining several lookups together. – chepner Mar 01 '19 at 14:17
  • @chepner - the type signature of `String->IO (Maybe Int)` is what "suggests `f` has to return an `IO` value". If it had a type signature of `String->Maybe Int` it would be fine. – Robin Zigmond Mar 01 '19 at 14:18
  • 2
    @RobinZigmond That's what I meant; the type signature is imposing an external constraint on the return type that isn't otherwise suggested by its argument or the lists it operates on. – chepner Mar 01 '19 at 14:19
  • 1
    @BercoviciAdrian I just looked up that part of the chapter. Nowhere is the code you have put there listed. There are similar examples involving chaining `lookup` calls inside a `do` block, but nowhere is a type signature involving `IO` given for it. I think you are getting confused and putting things together that shouldn't be together. – Robin Zigmond Mar 01 '19 at 14:19
  • 1
    Even simpler (though requiring an import from `Control.Monad`): `f = let lookup' = flip lookup in lookup' amap >=> lookup' bmap >=> lookup' final`. – chepner Mar 01 '19 at 14:23
  • I am sorry for the confusion and the book example.I thought `do` can only be used in the `IO` monad hence i defined my method return type. as `IO( Maybe Int)`.Apologies – Bercovici Adrian Mar 01 '19 at 14:29
  • 1
    @BercoviciAdrian - no need to apologise, I'm always happy to help and I'm sure that applies to the others who've commented as well. For what it's worth, I wouldn't recommend RWH as the sole resource for learning Haskell. The best "book-style" resources for beginners that I've come across are [Learn You a Haskell](http://learnyouahaskell.com/chapters) and the [Haskell wikibook](https://en.wikibooks.org/wiki/Haskell) (the more advanced chapters of that are very patchy but still full of good information, but the beginner-to-intermediate stuff is very good). – Robin Zigmond Mar 01 '19 at 14:47
  • Yes i have looked through `Learned you a haskell` but what i come to realise is that after i finish a chapter with `RWH` even though its harder , there's nothing left to learn on that chapter in other books / tutorials( + i am halfway though) .For me it looks as a hardcore-complete resource (deprecated in some parts) – Bercovici Adrian Mar 01 '19 at 14:58

1 Answers1

3

For the given type signature, you need to lift the value returned by the final call to lookup into an IO value.

f::String->IO (Maybe Int)
f par = return $ do
      a <- lookup par amap
      b <- lookup a bmap
      lookup b final

The do expression is syntactic sugar for uses of >>= that operate in the Maybe monad, not IO, in this case, but the return takes the resulting Maybe Int value and produces the required IO (Maybe Int) value. It could be desugared as

f par = return (lookup par amap >>= \a -> lookup a bmap >>= \b -> lookup b final)

However, aside from the type signature forcing you to use return, there is nothing else about f that requires it to return an IO value; neither its argument nor any of the three association lists involve IO in any way, so you could simply change the type signature to eliminate any reference to IO and operate purely in the Maybe monad.

f :: String -> Maybe Int
f par = do
    a <- lookup par amap
    b <- lookup a bmap
    lookup b final

An aside: getting rid of do

Since you are chaining together various calls to lookup, it would be nice if you could feed each result to the next call in a point-free style. In order to do this, you need to reverse the order in which lookup accepts its arguments. This is trivial to do using flip: flip lookup :: [(a,b)] -> a -> Maybe b.

f :: String -> Maybe Int
f par = let lookup' = flip lookup
        in lookup' amap par >>= lookup' bmap >>= lookup' final

Taking this a step further, you can remove the reference to par altogether by using the >=> operator imported from Control.Monad. Compare its type to that of >>=:

:t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
:t (>=>)
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c

Instead of starting with Maybe value, you compose calls to lookup', and give your starting string to the function.

f :: String -> Maybe Int
f = let lookup' = flip lookup
     in lookup' amap >=> lookup' bmap >=> lookup' final
chepner
  • 497,756
  • 71
  • 530
  • 681