Consider the following example Racket code:1
(define (gt arr ptr) (list arr (add1 ptr))) (define (eq arr ptr) (list arr ptr)) (define (lt arr ptr) (list arr (sub1 ptr)))
This is getting a bit repetitive, so our instinct as lisp programmers might be to write a macro that captures the repetition. The only things that change across definitions are the function name, and the thing that it does to
ptr, which we can call
(define-macro (ptr-motion NAME OP) #'(define (NAME arr ptr) (list arr (OP ptr))))
With this macro, now we can define our above operations in terms of their common functionality, which makes this much simpler code. Also, if we ever want to extend this code (as I did to include
eq) it’s better to trust the macro than attempt to rewrite possibly-complicated functionality after not looking at it for a while.
(ptr-motion gt add1) (ptr-motion eq id) (ptr-motion lt sub1)
This is actually a case where code-writing macros are a bit overkill, though. All we really need are higher-order functions (so we can pass around
OP), and some way to easily create functions from other functions, but with some arguments applied. Because all functions are curried in Haskell, it becomes extremely easy to write this macro as a plain function, and then create the operations we want by partial application to that function.
We also lose the
NAME argument, since we can just use those names directly.