Before the lab
- Make sure you completed, and understood the section on match in L05
- Look through the previous labs on Racket, make note of any questions.
Background
Review
- Time
- 5 minutes
- Activity
- Group-Discussion.
- Questions about A2
- Questions about the quiz
Macros
In this lab, we'll discuss the construction of programs using macros. This is sometimes referred to as metaprogramming. Other prominent examples of metaprogramming include C++ templates and Reflection in Java
Some Racket forms like and
and or
look like function calls, but we
know they are not because they support short-circuit evaluation. We
can define forms that behave differently than function calls (or that
look like function calls, but actually do other things as well, like
the rackunit
check-
forms). These forms are sometimes called
macros.
Single Pattern Macros: define-syntax-rule
- Time
- 15 minutes
- Activity
- Demo, Group-Discussion.
start a new file
~/fcshome/cs2613/labs/L07/short-circuit.rkt
. Make the directory if needed.The simplest way of defining a macro in racket is to use define-syntax-rule. As a first approximation, you can think of these syntax rules as functions that don't evaluate their arguments.
Let's start by defining
And
, which is just like the lower case version except the short-circuit evaluation is from the right.according to the docs
check-exn
takes a thunk as an argument, which is just a function with zero parameters.
#lang racket
(define-syntax-rule (And a b)
(if b a #f))
(module+ test
(require rackunit)
(define (die)
(error 'die "don't run this"))
(check-equal? (And (die) #f) #f)
(check-exn exn:fail? (lambda () (and (die) #f))))
- Notice the use of
lambda
to delay evaluation incheck-exn
; that's exactly the kind of boilerplate we can eliminate with a syntax rule:
(module+ test
(define-syntax-rule (check-fail expr)
(check-exn exn:fail? (lambda () expr)))
(check-fail (and (die) #f))
(check-fail (And #f (die))))
- Macros are a key part of the implimentation strategy of racket. What's an example of some syntax we saw already that is (probably) implimented as a macro?
Redefining or
- Time
- 15 minutes
- Activity
- Individual work
- Your task is define
Or
in terms ofif
in a similar way toAnd
. It should pass the following tests
(module+ test
(check-equal? (Or #t #t) #t)
(check-equal? (Or #f #t) #t)
(check-equal? (Or #t #f) #t)
(check-equal? (Or (die) #t) #t)
(check-fail (or (die) #t)))
Multiple patterns and recursion
- Time
- 15 min
- Activity
- Demo
We have discussed the difference between let
and let*
a bit in the
labs. It turns out that let*
can be implimented in terms of let
using a macro.
Let's start by reviewing let*
:
(module+ test
(check-equal? (let* ([x 5]
[y (- x 3)])
(+ y y))
4)
(check-equal? (let* ([x 5]
[y (- x 3)]
[y x])
(* y y))
25))
The key insight is that
let*
behaves like a nested set oflet
s.A natural tool to impliment this is recursion. To make recursive macros we need to use
define-syntax
, along withsyntax-rules
(roughly speakingmatch
for macros). Let's try to reverse engineer the patterns for thissyntax-case
statement. These are analogous tomatch
, and we need every identifier used in expanded expression to occur in the pattern.
#lang racket
(define-syntax Let*
(syntax-rules ()
[(Let* () ) body]
[(Let* ([ first-val] [id val] ...) body)
(let ([first-id ])
(Let* ([id ] ...) ))]))
(module+ test
(require rackunit)
(check-equal? (Let* ([x 5] [y (- x 3)]) (+ y y)) 4)
(check-equal? (Let* ([x 5] [y (- x 3)] [y x]) (* y y)) 25))
- Time permitting, try the example code in the macro stepper. Make
sure you have
Macro hiding
set tostandard
.
Before you start the quiz
Commit and push the .rkt
files you wrote so far today.
Quiz
I will hand out the quiz instructions on paper.