Entropic Thoughts

Non-Obvious Haskell Idiom: Bind to Lambda Case

Non-Obvious Haskell Idiom: Bind to Lambda Case

Reading production Haskell code, we sometimes stumble over idioms that look confusing at first, but which show up frequently enough that they are worth learning. We have already seen

Today we’ll continue the theme of the previous article, where we try not to give names to intermediary values that are not meant to be used. The way we do this is with the bind operator and the LambdaCase language extension. This means we write code that says

getQueueMember :: Int -> ExceptT Text QueueAccess Member
getQueueMember position =
  lift (Queue.getAt position) >>= \case
    Nothing -> throwError "That position holds no member."
    Just c -> pure c

Instead of

getQueueMember :: Int -> ExceptT Text QueueAccess Member
getQueueMember position = do
  maybeMember <- lift (Queue.getAt position)
  case maybeMember of
    Nothing -> throwError "That position holds no member."
    Just c -> pure c

where the latter leaks an identifier maybeMember that’s only used in the pattern match. I don’t think this idiom requires very much explanation. It’s useful to burn the comparison above into the right neural circuits, to make it easier to decode the more cryptic version when encountered.

In this specific case, we could even get rid of the pattern match and use the function that deconstructs a Maybe value:

getQueueMember :: Int -> ExceptT Text QueueAccess Member
getQueueMember position =
  lift (Queue.getAt position) >>= maybe
    (throwError "That position holds no member.")
    pure

This does the same thing except pawns off the pattern matching to the maybe function.

This idiom also comes in more advanced flavours, e.g. where a small chunk of code is wrapped in runMaybeT to get short-circuiting behaviour in the results from otherwise monadic calls. I intend to write a separate article on that idiom, though, because it will require more explanation.