Macros
The idea is pretty simple:
We write code, and sometimes we repeat ourselves there. So why not write code that abstracts over this repetition?
That is what Macros are.
§ C Macros
The lowest sophistication level (and the worst to work with) is something like C preprocessor macros. There, you do the easiest thing possible: A string replacement over the full following program text, irrespective of anything else.
So a Macro in C has this type signature:
CMacro :: String -> String#define LALA "hohohoho"
char laugh[] = LALA->
char laugh[] = "hohohoho"A standard C Macro usage looks like this:
#define min(X, Y) ((X) < (Y) ? (X) : (Y)) x = min(a, b); → x = ((a) < (b) ? (a) : (b)); y = min(1, 2); → y = ((1) < (2) ? (1) : (2)); z = min(a + 28, *p); → z = ((a + 28) < (*p) ? (a + 28) : (*p));But since this is just doing straight string replacement, anything can happen (There’s just no rules whatsoever for the expansion, so you can have unbalanced parentheses and other weird stuff).
They’re still powerful, and you probably want to use them in C, but we can do better.
§ Lisp Macros
Lisp Macros do better: They’re not doing string replacement, but operate on the syntax tree.
LispMacro :: Syntax -> SyntaxThe also have a second pretty helpful property: They use the same syntax as the base language. So their type is actually this:
LispMacro :: LispSyntax -> LispSyntaxThis is what they look like:
> (defmacro the-macro (a b) `(+ ,a (* ,b 3)))
> (the-macro 4 5) ;; => 19This makes them way more generally useful (for example)
But they, too, have a problem: They’re not hygienic.
That is, they’re introducing variables into their target program that could be referenced both from the inside and outside.
This possibly couples the macro source code to the location it’s being used in, which, if you squint, is the one unambigiously bad (default) feature in programming languages (again): Dynamic Scoping
There’s an easy fix for this, however: gensym
But this makes for a relatively bad default, and we shouldn’t rely on programmers knowing all the ways in which they could fuck up, and then having the mental workload left to fix them.
Writing in here is haphazardous, disjointed and sketchy.
It's probably a good idea to come back later.
§ Scheme Macros
Scheme was the first language that didn’t have the problem of accidental scope leaking. They introduced hygienic macros.
§ Racket Macros
Racket escalates this completely, and has facilities to define whole new languages.
Writing in here is haphazardous, disjointed and sketchy.
§ Notes
- I’ve skipped procedural macros (never heard of them before)
- Damn, are those AI Memos badly scanned sometimes: https://dspace.mit.edu/bitstream/handle/1721.1/6111/AIM-057.pdf?sequence=2&isAllowed=y