Speed

Generate code

LHS of a pattern match

Type checking with rank-2 types

Conclusion

Haskell has higher-order functions, and lazy evaluation, but not macros. Does Haskell need macros? For the purpose of this, I'm talking about macro in the "a little piece of code that expands out" sense, rather than the conditional compilation that macros quite often get used for in C++.If Haskell needs macros then there must be some benefit to adding them. The reason that a lot of languages need macros is because they have strict evaluation. Consider trying to write a function that selects one of two arguments, based on the first condition variable - some sort of if:iff a b c = if a then b else cYou can write this in Haskell. You can also write this in VB (and it is pre-defined), but it doesn't do what you might expect in VB . Haskell's lazy evaluation is a big win here, opening up entirely new opportunities.So what are the reasons to add macros?This is just a silly argument. First off, a pure function can always be inlined away, so performance is not an issue. A compiler might choose not to inline a function, but you can usually persuade it that it should with pragma's etc. And finally, there is the real possibility that you (the author of code) doesn't have a clue what should be inlined and is actually throwing away speed for premature optimisation.One thing that macros can do is to generate definitions, functions can be used to generate expressions (essentially a function is an expression with some pre-defined structure). You can't define a Haskell function that takes some data, and generates classes/instances etc. For example, imagine that you want to define lots of constructors for binary operations. Instead of:data Expr = Add Expr Expr| Mul Expr Expr...Here a macro might let you write something like:data Expr = binary(Add) | binary(Mul) ...Of course, this is something you really can't do with Haskell as it stands. However most of the ways you might use a macro like this are covered by Haskell. There are type synonyms, which may actually let you do the above trick with GADT's (I haven't tried, but its certainly feasible they could work like that, even if they currently don't). Classes has defaults which is another situation where otherwise this functionality might be required.For most purposes generating code with macros is not about doing clever stuff, but about cutting out repetitive stuff. Haskell has (by design) cut out most of the repetitive tasks, so no longer requires macros. For truly repetitive tasks, Template Haskell can be used, or things like Drift and Scrap Your Boilerplate. All of these techniques reduce the number of places where macros might otherwise be required.One place where expressions are present, but functions are not allowed is on the left hand side of a pattern match. For example:f 3.14159265358979 = 2.71828182845905Of course, it's silly to write this using hardcoded values, what if either of these constants change?f pi = exp 1Unfortunately without macros, this is not allowed in Haskell since pi is a function (a constant arity function, but still a function). You'd instead have to write:f x | pi == x = exp 1Not too painful, but what if you are trying to match a LHS with a free variable in it:f (Neg (Neg (Neg a))) = Pos $ Pos $ Pos aHere you are matching a deeply nested constructor, and on the right have to explicitly bracket the terms. However on the right you are able to use the function ($) which due to precedence issues removes the need for brackets. One thing you can't do however is:f (Neg $ Neg $ Neg a) = Pos $ Pos $ Pos aIf ($) was a macro, this would be valid however.Haskell has no feature here that can reduce the redundancy in the pattern match.But while macros would be useful here, how far would standard C style macros take us? Let's consider defining a useful auxiliary:apply 0 f x = xapply n f x = f (apply (n-1) f x)Now we can rewrite this as:f (Neg (Neg (Neg a))) = apply 3 Pos xBut we really can't write:f (apply 3 Neg a) = apply 3 Pos xHowever, its entirely possible to come up with a consistent semantics for this, so maybe one day this might be something people decide is useful.For the moment the lack of macros on the LHS is occasionally annoying, but generally is not a feature that anyone is crying out for. The LHS is rarely sufficiently complex for macros to show great benefit.While standard Haskell (mainly, ignoring MR and seq - for those who care) has the fold and unfold law, i.e. you can inline a function and then contact it back, once you introduce rank-2 types you loose this property. Unfold still holds, but fold doesn't. This leads to lots of typing problems for things like runST which present rank-2 types to the user.While macros would paper over the cracks here, the real solution probably lies in the type system, which I believe GHC has in various flavours of impredictive polymorphism. The other solution is not to use rank-2 types, something I have been happily avoiding for years now.Despite macros being useful in languages like Lisp, in Haskell their usefulness is probably minimal. Lazy evaluation is often a substitute for macros, and where other languages require macros to define domain specific languages, Haskell solves these problems naturally.Macros are quite a painful cost to a language, they reduce the assumptions that people can make and require people to play hunt the macro. Haskell really doesn't need them!