listhaskelllist-comprehensionmonadsguard-statement

Understanding the guard function and list comprehension


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?


Solution

  • In the list Monad instance:

    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 []