I'm trying to implement a function which converts a string to a list of Maybe Ints, e.g. readInts "1 2 42 foo" = [Just 1,Just 2,Just 42,Nothing]
.
My first aproach was:
readInts (s::String) = do {
ws <- words s;
return (map (readMaybe::(String -> Maybe Int)) ws)
}
This resulted in the following error:
lab_monad.hs:20:52:
Couldn't match type ‘Char’ with ‘[Char]’
Expected type: [String]
Actual type: String
In the second argument of ‘map’, namely ‘ws’
In the first argument of ‘return’, namely
‘(map (readMaybe :: String -> Maybe Int) ws)’
Failed, modules loaded: none.
What I tried next (and worked), was:
readInts (s::String) = do {
let ws = (words s) in do
return (map (readMaybe::(String -> Maybe Int)) ws)
}
My question here is, words s
obviously is of type [String]
. Why does the interpreter say it is a String
? What am I not understanding about <-
operator?
ws <- words s
, in the list monad, nondeterministically assigns one word from words s
to ws
; the remaining code simply works with that one word, and the return
function "magically" combines the results of working on all the words into the result list.
readInts s = do
ws <- words s -- ws represents *each* word in words s
return (readMaybe ws)
The do
notation is just syntactic sugar for using monadic bind
:
readInts s = words s >>= (\ws -> return (readMaybe ws))
Without using the Monad
instance for lists, you can use map
to apply the same function to each word.
readInts s = map readMaybe (words s)
let
, on the other hand, simply provides a name for a more complicated expression to be used in another expression. It can be considered syntactic sugar for defining and immediately applying an anonymous function. That is,
let x = y + z in f x
is equivalent to
(\x -> f x) (y + z)
^ ^ ^
| | |
| | RHS of let binding
| part after "in"
LHS of let binding
A let
statement with multiple bindings is equivalent to nested let
statements:
let x = y + z
a = b + c
in x + a
is equivalent to
let x = y + z
in let a = b + c
in x + a
which desugars to
(\x -> (\a -> x + a)(b + c))(y + z)