Block Arguments

The release of GHC 8.6.1 brought the release of a language extension enabling an apparently small syntactic change: the BlockArguments extension. During the discussion and implementation phase, it was also sometimes known as ArgumentDo or occasionaly ArgumentBody.

In standard Haskell 2010, some expressions cannot be used as arguments to functions without parentheses or, perhaps more idiomatically, a dollar sign between the function and its argument.The expressions under discussion can be used as arguments to operators without parentheses or the dollar sign. Consider the difference between fmap (+1) (Just 3) and (+1) <$> Just 3. In the first case, the parentheses are necessary around Just 3, although this is an acceptable substitute: fmap (+1) $ Just 3. In either case, we use some punctuation to give the parser a little help. But with the infix operators, the precedence and fixity rules often obviate the need for it. These include do blocks and mdo blocks enabled by the RecursiveDo extension; case expressions; if-then-else expressions; lambda expressions; let expressions; and the \case expressions enabled by LambdaCase. But Haskellers will do nearly anything to avoid using parentheses, so now we have a syntactic extension that allows these expressions to be parsed directly, without parentheses or $, as arguments to the function.

It is worth noting before we move on to examples that ambiguities arising from use of this extension are resolved by reading the expression as extending as far to the right as possible. Thus, f \a -> a b will be parsed as f (\a -> a b), not as f (\a -> a) b.BlockArguments GHC User Guide documentation.

With do blocks

This is the most typical use case for this extension, because using a do block as an argument to a function is so common.

Consider a typical usage of forever with a do block argument:

main :: IO ()
main =
  forever $ do
    word <- fmap fixString getLine
    isPalindrome word

With the BlockArguments extension enabled, it can now be written:

{-# LANGUAGE BlockArguments #-}

main :: IO ()
main =
  forever do
    word <- fmap fixString getLine
    isPalindrome word

What will you do with all your extra dollars now that you no longer need them for this?

If you remove the $ but forget to enable the extension, the compiler will helpfully remind you that that’s probably what you were trying to do:

    Unexpected do block in function application:
        do word <- fmap fixString getLine
           isPalindrome word
    You could write it with parentheses
    Or perhaps you meant to enable BlockArguments?
25 |   do
   |   ^^...

The extension permits multiple block arguments as well.This example is fun in the REPL. To open a REPL that will run it, try stack repl --package async --resolver nightly. Concurrency is fun.

{-# LANGUAGE BlockArguments #-}

import Control.Concurrent.Async (concurrently_)

blockStack =
    putStrLn ['a'..'z']
    putStrLn ['A'..'Z']

With lambdas

As we noted above, this extension works with some other expressions. We’ll look next at how you could use it with the example we give on the LambdaCase page.

{-# LANGUAGE LambdaCase #-}

lambdaize = fmap (\case 'l' -> 'λ'
                        x   -> x)

Enabling BlockArguments allows you to get rid of those parentheses. Unfortunately, it does not relieve you of the need to indent your cases properly!

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE LambdaCase #-}

lambdaize3 :: [Char] -> [Char]
lambdaize3 = fmap \case 'l' -> 'λ'
                        x   -> x


The idea for this extension appears to have originated in an email from Andrew Gibiansky to the haskell-cafe mailing list. The original email and the original Trac ticket.

In terms of Haskell 2010, this extension reclassifies the class of nonterminal expressions called lexp as aexp; aexp are expressions allowed as arguments. See this section of the Haskell 2010 Report for details about the expressions; this GHC page has further comments about the change.

Join Type Classes for courses and projects to get you started and make you an expert in FP with Haskell.