Macros can provide a lot of syntactic convenience over those first-class functions, especially with heavily nested structures. For example, I can replace this monadic parser definition...
function SpecDeclP()Parser{
return Bind(SeqRight(MyKeywordP, IdentifierP), function(nm interface{})Parser{
return Bind(BetweenParensP(IdentifierP), function(parm interface{})Parser{
return Bind(SetSwitchUserStateP, function(_ interface{})Parser{
return Bind(BlockP, function(bod interface{})Parser{
return Result("func " + nm.(string) + "(" + parm.(string) + ")" + bod.(string))
})
})
})
})
}
...with this more readable version if I have a recursively defined "macro" called doparse... doparse{
nm <- SeqRight(MyKeywordP, IdentifierP)
parm <- BetweenParensP(IdentifierP)
SetSwitchUserStateP
bod <- BlockP
Result("func " + nm.(string) + "(" + parm.(string) + ")" + bod.(string))
}
This requirement arose in Haskell for its Parsec before it became a part of the syntax, then again later with the Arrow library which later also added it to the syntax. Whenever new abstractions are discovered/created, a macroing facility, whether for lisp-like syntax or some other, helps makes all the nested functions more readable.