haskellmonadssyntactic-sugarlist-definition

Haskell - How the construct do calls fail function from Monad?


While studying Monads I understood why when pattern matching on list definitions fails, its computation is somewhat "ignored" instead of throwing an error:

test :: [(Int, Int)]
test = [(x, y) | (Just x) <- [Just 1, Nothing, Just 3], y <- [1, 2]]

*Main> test
[(1,1),(1,2),(3,1),(3,2)]

It's because it is just a syntactic sugar for a Monad application using do:

test'' :: [(Int, Int)]
test'' = do
  (Just x) <- [Just 1, Nothing, Just 3]
  y <- [1, 2]
  return (x, y)

*Main> test'
[(1,1),(1,2),(3,1),(3,2)]
*Main> test == test'
True

Similarly, we could try to resemble this logic using the bind operator >>=:

test'' :: [(Int, Int)]
test'' = [Just 1, Nothing, Just 3] >>= \(Just x) -> [1, 2] >>= \y -> return (x, y)

However, as expected, the monadic function fail relative to List will not be called in this situation like it was in the previous ones:

*Main> test''
[(1,1),(1,2)*** Exception: test.hs:11:40-82: Non-exhaustive patterns in lambda

So, my question is: Is it possible to get [(1,1),(1,2),(3,1),(3,2)] using test'' style, in a neat way? Is do construct a syntactic sugar for something like this?

test'' :: [(Int, Int)]
test'' = [Just 1, Nothing, Just 3] >>= \maybeX -> [1, 2] >>= \y -> case maybeX of Just x -> return (x, y)
                                                                                  _      -> fail undefined

*Main> test''
[(1,1),(1,2),(3,1),(3,2)]
*Main> test'' == test
True

Solution

  • For instance

    {-# LANGUAGE LambdaCase #-}
    
    test'' = [Just 1, Nothing, Just 3] >>= \case
                Just x -> [1, 2] >>= \y -> return (x, y)
                _ -> fail "..."