Troy  Yao
Troy Yao

Reputation: 97

In haskell, how to transfer the functions defined without do notation to the function defined with do?

I am new in Haskell. Now I have some problems about type. It is said that the function:

fg a tb tc = case lookup a tb of
    Nothing -> Nothing
    Just b -> case lookup b tc of
        Nothing -> Nothing
        Just c -> Just [b,c]

is the same as the function:

fg' a tb tc = do
    b <- lookup a tb
    c <- lookup b tc
    Just [b,c]

Their type are both fg :: (Eq t, Eq a) => a -> [(a, t)] -> [(t, t)] -> Maybe [t]

However, the function below is treated as a different one:

fg'' a tb tc = do
    let     
        b = lookup a tb
        c = lookup b tc
    Just [b,c]

Its type is fg'':: (Eq b, Eq a) => a -> [(a, b)] -> [(Maybe b, b)] -> Maybe [Maybe b]. These confuses me. Do anyone can explain why?

Upvotes: 0

Views: 191

Answers (3)

Luka Horvat
Luka Horvat

Reputation: 4412

In do notation, the <- takes the value out of the Monad (Maybe in this case) and let's you manipulate that value in the code that follows. But that's not the only thing that it does.

It also applies the 'characteristic' of the Monad in question. The characteristic of maybe is that if the value on the right side of the arrow is Nothing, it doesn't do any unwrapping (because there's nothing to unwrap) and just fails the whole computation, so that the result of the whole thing is also Nothing.

A let binding doesn't do anything like that. It just assigns a new name to some existing value so when you do let a = lookup b tb the a is still of the type Maybe something. On the other hand, when you unwrap it like a <- lookup b tb, the type of a is something.

Upvotes: 1

chi
chi

Reputation: 116174

The monad laws ensure that

do x <- return y   -- note the return here
   ...

is equivalent to

do let x = y
   ...

Note, however, that without the return above, the two xs in the code above have a different type, so they can never be equivalent. This is because if y :: m a, then the first piece of code has x :: a, while the second uses x :: m a, and a and m a are always different types.

For instance,

do x <- [1]
   return x

evaluates to [1]. Instead,

do let x = [1]
   return x

evaluates to [[1]].

Upvotes: 3

bheklilr
bheklilr

Reputation: 54078

You're saying that b = lookup a tb. This means that b has the same type as lookup a tb, and lookup :: Eq a => a -> [(a, b)] -> Maybe b. So b :: Maybe b. Next, c = lookup b tc means that c has the same type as lookup b tc. Since b :: Maybe b, lookup b :: [(Maybe b, c)] -> Maybe c (remember that the type variables chosen are irrelevant, so long as they're consistent), and c :: Maybe c. Since lists in Haskell are homogeneous, [b, c] means that b and c have the same type, so Maybe b ~ Maybe c implies b ~ c. Since fg'' returns a value of Just [b, c], this means that Just [b, c] :: Maybe [Maybe b].

The <- syntax in Haskell is not equivalent to = in a let. In your case, fg' could be turned into a function without do using the >>= operator, which for Maybe has the type

(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b

And can be used as

fg''' a tb tc =
    lookup a tb >>= (\b ->            -- b <- lookup a tb
        lookup b tc >>= (\c ->        -- c <- lookup b tc
            Just [b, c]               -- Just [b, c]
        )
    )

Think of >>= as feeding the value wrapped in a monad forward into a function that returns a new monadic value. For Maybe, this operator will short circuit if the Maybe a turns out to be Nothing, since it won't have a value to feed forward into the (a -> Maybe b).

Upvotes: 3

Related Questions