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:
With the BlockArguments
extension enabled, it can now be written:
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:
error:
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.
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.
Enabling BlockArguments
allows you to get rid of those parentheses. Unfortunately, it does not relieve you of the need to indent your cases properly!
History
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.