I am learning about the guard
function from the book 'Learn You a Haskell for Great Good!' by Miran Lipovaca.
For the following example:
ghci> [1..50] >>= (\x -> guard('7' `elem` show x) >> return x)
[7, 17, 27, 37, 47]
I know that guard
takes a Boolean value, and if the value is True
, guard takes ()
and puts it in a minimal default context and succeeds.
If the value is False
, then guard
makes a failed monadic value.
However, I don't understand how guard works in the above example to create the resulting list [7, 17, 27, 37, 47]
. What is passed as x
in the lambda function, is it 1? Further, if ('7' `elem` show x)
evaluates to False
, then wouldn't the empty list be returned? How exactly does the final result list come to be?
In the list Monad
instance:
>>=
is concatMap
with the arguments flipped
guard condition
is equivalent to if condition then [()] else []
And in any Monad
instance, a >> b
= a >>= \_ -> b
, so in the list instance this is equivalent to concatMap (\_ -> b) a
.
Therefore your code desugars to this:
concatMap
(\x -> concatMap
(\_ -> [x])
(if '7' `elem` show x then [()] else []))
[1..50]
So the outer concatMap
produces as an intermediate value a list of 50 elements, each being a list, which is a singleton list of the input value if its string representation contains a digit 7
, or else the empty list:
[[], [], [], [], [], [], [7], [], [], [], [], [], [], [], [], [], [17], …]
Which is then concatenated to produce the final result [7, 17, 27, 37, 47]
.
What is passed as x in the lambda function, is it 1?
It’s each element of the input list, 1
through 50
.
The inner concatMap
produces [x]
if the condition is true and []
if the condition is false, because guard
produces a list of one element (the dummy ()
) if the condition is true and an empty list if it’s false—this may be easier to see if you rephrase it as an equivalent:
map (\_ -> x) (if '7' `elem` show x then [()] else [])
-- or
if '7' `elem` show x then [x] else []