Reputation: 97
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
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
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 x
s 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
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