I frequently use Racket's pattern-matching construct match, and I thought a way to help myself with debugging programs using match, and to learn how Racket/Scheme macros work, would be to create a macro that includes information like which pattern was matched.
In other words, I'm looking to create a macro that, given this:
(match/debug 'two
['one 1]
['two 2])
Outputs something like this:
Case 2 <-- Printed
2 <-- Returned value
The main obstacle so far has been trying to get the numbers signifying the resolved case to show up correctly.
My target was to try to write something that would expand like this:
(match 'two
['one (displayln "Case 1") 1]
['two (displayln "Case 2") 2])
But I haven't been able to figure out any way to generate those "Case #" strings.
Here is my attempted macro definition:
(define-syntax-rule (match/debug id [pattern value] ...)
(let ([index 0])
(match id
[(begin
(set! index (add1 index))
pattern)
(printf "Case ~a\n" index)
value] ...)))
It appears as though the syntax of match won't let me do something like this, but this was the only way I could think of. I'm only really used to Common Lisp's style of macros.
Here is a solution.
The helper function clauses->numbers returns a list of the numbers from 0 to the one less than the number of clauses. This is then used to give each clause its own number. Note that this solution counts from 0 (and not 1 as in your examples).
#lang racket
(require (for-syntax syntax/parse))
(begin-for-syntax
(require racket/list) ; import range
(define (clauses->numbers stx)
(range (length (syntax->list stx)))))
(define-syntax (match/debug stx)
(syntax-parse stx
[(_match/debug id [pattern value] ...)
(with-syntax ([(n ...) (clauses->numbers #'([pattern value] ...))])
(syntax/loc stx
(match id
[pattern (begin (displayln (~a "Case: " n)) value)]
...)))]))
(match/debug 'one
['one 1]
['two 2])
(match/debug 'two
['one 1]
['two 2])
The output:
Case: 0
1
Case: 1
2
Related
Given a Chez Scheme record with many numeric fields that are contantly being mutated by small increments and decrements, usually by one, is there a way to write a macro that can mutate a field value by passing it the field? The way I accomplish this now is something like the following REPL transcript:
Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.
> (define-record-type r (fields (mutable x) (mutable y)
;; and so on...
))
> (define my-r (make-r 3 5
;; and so on...
))
> (r-x-set! my-r (+ (r-x my-r) 1))
> my-r
#[#{r gak6l6ll8wuv7yd61kiomgudo-2} 4 5]
It would be nice to have a simple macro, say inc!, that could do the mutating increment/decrement operations on the fields in the record. I started with something like a Scheme version of Lisps incf and decf,
(define-syntax inc!
(syntax-rules ()
((_ x) (begin (set! x (+ x 1)) x))))
(inc! (r-x my-r)) ;; Syntax error
Which works for "normal" variables (and makes it easy to implement dec!), but it doesn't use the mechanism to set mutable record fields, r-x-set! in this case.
Is there an obvious way to write such a macro? One where you can just pass a reference to the record field without having to write something different for each field?
You can construct a -set! mutator from the given accessor. This can be done by converting the symbol for the accessor to a string and appending "-set!" to it. Then eval can be used to get the actual mutator procedure. Here is a macro that increments a specified field by some amount n:
(define-syntax increment-n!
(syntax-rules ()
[(_ (acc rec) n)
(let* ((acc-name (symbol->string (quote acc)))
(mut-name (string-append acc-name "-set!"))
(mut! (eval (string->symbol mut-name))))
(mut! rec (+ (acc rec) n)))]))
This can be used to create an inc! macro:
(define-syntax inc!
(syntax-rules ()
[(_ (acc rec)) (increment-n! (acc rec) 1)]))
But, it would be nice to be able to increment multiple fields at the same time; here are inc! and dec! macros that do that:
(define-syntax inc!
(syntax-rules ()
[(_ (acc rec) ...) (begin (increment-n! (acc rec) 1) ...)]))
(define-syntax dec!
(syntax-rules ()
[(_ (acc rec) ...) (begin (increment-n! (acc rec) -1) ...)]))
Sample interaction:
> my-r
#[#{r n5an6pxs3wvid36v2gvn8z9zo-5} 3 5 7]
> (inc! (r-x my-r))
> my-r
#[#{r n5an6pxs3wvid36v2gvn8z9zo-5} 4 5 7]
> (dec! (r-z my-r))
> my-r
#[#{r n5an6pxs3wvid36v2gvn8z9zo-5} 4 5 6]
> (inc! (r-x my-r) (r-y my-r) (r-z my-r))
> my-r
#[#{r n5an6pxs3wvid36v2gvn8z9zo-5} 5 6 7]
A Note on the Use of eval
The increment-n! macro constructs a symbol which has already been bound to a mutator procedure. That symbol could then be bound to mut! directly, but then when the expression (mut! rec (+ (acc rec) n)) is evaluated an exception would be raised since mut! now evaluates to a symbol, e.g., r-x-set!. We want mut! to evaluate to a procedure in a procedure call. By calling eval on the constructed symbol first we get the mutator procedure which is bound to that symbol, binding it to mut! instead of the symbol.
Here is a REPL interaction that illustrates the problem, and will hopefully help to clarify:
> (define f (string->symbol "+"))
> f
+
> (f 1 2)
Exception: attempt to apply non-procedure +
Type (debug) to enter the debugger.
> (define f (eval (string->symbol "+")))
> f
#<procedure +>
> (f 1 2)
3
As an exercise in learning the Racket macro system, I've been implementing a unit testing framework, based on the C++ catch framework. One of the features of that framework is that if I write a check like this:
CHECK(x == y); // (check x y)
When the check is violated the error message will print out the values of x and y, even though the macro used is completely generic, unlike other test frameworks that require you to use macros like CHECK_EQUALS, CHECK_GREATER, etc. This is possible through some hackery involving expression templates and operator overloading.
It occurs to me that in Racket you should be able to do an even better job. In the C++ version the macro can't see inside subexpressions, so if you write something like:
CHECK(f(x, g(y)) == z); // (check (= (f x (g y)) z))
When the check is violated you only find out the values of the left and right hand side of the equal sign, and not the values of x, y, or g(y). In racket I expect it should be possible to recurse into subexpressions and print a tree showing each step of the evaluation.
Problem is I have no idea what the best way to do this is:
I've gotten fairly familiar with syntax-parse, but this seems beyond its abilities.
I read about customizing #%app which almost seems like what I want, but if for example f is a macro, I don't want to print out every evaluation of the expressions that are in the expansion, just the evaluations of the expressions that were visible when the user invoked the check macro. Also not sure if I can use it without defining a language.
I could use syntax-parameterize to hijack the meaning of the basic operators but that won't help with function calls like g(y).
I could use syntax->datum and manually walk the AST, calling eval on subexpressions myself. This seems tricky.
The trace library almost looks like what it does what I want, but you have to give it a list of functions upfront, and it doesn't appear to give you any control over where the output goes (I only want to print anything if the check fails, not if it succeeds, so I need to save the intermediate values to the side as execution proceeds).
What would be the best or at least idiomatic way to implement this?
Here is something to get you started.
#lang racket
(require (for-syntax syntax/parse racket/list))
(begin-for-syntax
(define (expression->subexpressions stx)
(define expansion (local-expand stx 'expression '()))
(syntax-parse expansion
#:datum-literals (#%app quote)
[x:id (list #'x)]
[b:boolean (list #'b)]
[n:number (list #'n)]
; insert other atoms here
[(quote literal) (list #'literal)]
[(#%app e ...)
(cons stx
(append-map expression->subexpressions (syntax->list #'(e ...))))]
; other forms in fully expanded syntax goes here
[else
(raise-syntax-error 'expression->subexpressions
"implement this construct"
stx)])))
(define-syntax (echo-and-eval stx)
(syntax-parse stx
[(_ expr)
#'(begin
(display "] ") (displayln (syntax->datum #'expr))
(displayln expr))]))
(define-syntax (echo-and-eval-subexpressions stx)
(syntax-parse stx
[(_ expr)
(define subs (expression->subexpressions #'expr))
(with-syntax ([(sub ...) subs])
#'(begin
; sub expressions
(echo-and-eval sub)
...
; original expression
(echo-and-eval expr)))]))
(echo-and-eval-subexpressions (+ 1 2 (* 4 5)))
The output:
] (+ 1 2 (* 4 5))
23
] +
#<procedure:+>
] 1
1
] 2
2
] (#%app * '4 '5)
20
] *
#<procedure:*>
] 4
4
] 5
5
] (+ 1 2 (* 4 5))
23
An alternative to printing everything is to add a marker for stuff that should be shown. Here's a rough simple sketch:
#lang racket
(require racket/stxparam)
(define-syntax-parameter ?
(λ(stx) (raise-syntax-error '? "can only be used in a `test' context")))
(define-syntax-rule (test expr)
(let ([log '()])
(define (log! stuff) (set! log (cons stuff log)))
(syntax-parameterize ([? (syntax-rules ()
[(_ E) (let ([r E]) (log! `(E => ,r)) r)])])
(unless expr
(printf "Test failure: ~s\n" 'expr)
(for ([l (in-list (reverse log))])
(for-each display
`(" " ,#(add-between (map ~s l) " ") "\n")))))))
(define x 11)
(define y 22)
(test (equal? (? (* (? x) 2)) (? y)))
(test (equal? (? (* (? x) 3)) (? y)))
which results in this output:
Test failure: (equal? (? (* (? x) 3)) (? y))
x => 11
(* (? x) 3) => 33
y => 22
Is there any way to detect if a macro is expanding inside a pattern match?
Here's an example macro that I'd like to write, but it fails inside a match-define:
#lang racket/base
(require racket/match (for-syntax racket/base syntax/parse))
(struct point (x y))
(define-syntax (friendly-point stx)
(syntax-parse stx
[(_ arg* ...)
#'(begin (printf "Now making a point\n") (point arg* ...))]
[_ #'(begin (printf "Hello point\n") point)]))
(define p (friendly-point 1 2))
;; Prints "Now making a point"
(match-define (friendly-point x y) p)
;; ERROR
Yes. Instead of using an ordinary syntax transformer created with define-syntax, use define-match-expander to create a macro that can cooperate with match.
(require (for-syntax syntax/parse))
(define-match-expander positive
(syntax-parser
[(_ n)
#'(? positive? n)]))
(match 3
[(positive n) (~a n " is positive")])
; => "3 is positive"
The define-match-expander form is flexible: it can be used to create macros that may only be used inside of match, but it can also be used to create macros that expand differently depending on how they are used by providing two transformer functions, one for each context. This allows you to have “context-sensitive” identifiers which work as both functions and as match expanders.
(require (for-syntax syntax/parse)
(prefix-in base: racket/base))
(define-match-expander syntax
(syntax-parser
[(_ x)
#'(? syntax? (app syntax->datum x))])
(make-rename-transformer #'base:syntax))
(match (syntax (1 2 3))
[(syntax n) (~a n " is a syntax list")])
If you need even more flexibility, you can forego define-match-expander entirely and define a custom struct with the prop:match-expander structure type property. This can be combined with prop:procedure to achieve the two-argument functionality described above, but it can also hold state, and it can be paired with other structure type properties such as prop:rename-transformer to allow the same identifier to function in many, many different contexts.
What you are looking for is define-match-expander. It allows you to make a macro that is expanded inside of a pattern matching context. (Also, because it takes in two thunks, you can have a variant that is also used when not in a matching context. Next, I should point out that
Next, you can have a printing side effect inside of the template of a define-match-expander, but you can have in the macro itself. (Note though that the side effect will not occur if your module has already been expanded. This is explain in more detail in this paper.
So, using match expanders, including a second function for use outside of match, you get the following code:
#lang racket/base
(require racket/match (for-syntax racket/base syntax/parse))
(struct point (x y))
(define-match-expander friendly-point
(lambda (stx)
(syntax-parse stx
[(_ arg* ...)
(printf "Now matching a point\n")
#'(point arg* ...)]
[_ #'point]))
(lambda (stx)
(syntax-parse stx
[(_ args* ...)
#'(begin (printf "Now making a point\n") (point args* ...))])))
(define p (friendly-point 1 2))
;; Prints "Now making a point"
(match-define (friendly-point x y) p)
;; Works fine now
In Common Lisp it is relatively easy to create a macro-defining macro. For example, the following macro
(defmacro abbrev (short long)
`(defmacro ,short (&rest args)
`(,',long ,#args)))
is a macro-defining macro, because it expands to another macro.
If we now put
(abbrev def defun)
in our program, we can write def instead of defun whenever we define a new function.
Of course, abbrev can be used for other things, too. For example, after
(abbrev /. lambda)
we can write (/. (x) (+ x 1)) instead of (lambda (x) (+ x 1)). Nice. (For detailed explanation of abbrev, see http://dunsmor.com/lisp/onlisp/onlisp_20.html)
Now, my questions are:
Can I write the macro-defining macros in Racket?
If I can, how to do that? (for example, how to write something similar to
abbrev macro in Racket?)
According to this part of the Racket Guide:
(define-syntax-rule (abbrev short long)
(define-syntax-rule (short body (... ...))
(long body (... ...))))
Quoting the above link:
The only non-obvious part of its definition is the (... ...), which
“quotes” ... so that it takes its usual role in the generated macro,
instead of the generating macro.
Now
(abbrev def define)
(abbrev /. lambda)
(def f (/. (x) (+ x 1)))
(f 3)
yields
4
FWIW, it works on Guile as well, so it's no Racket-specific thing.
ad 1. Yes.
ad 2. Your example can most easily be written
#lang racket
(define-syntax (abbrev stx)
(syntax-case stx ()
[(_ short long)
#'(define-syntax short (make-rename-transformer #'long))]))
(abbrev def define)
(def x 42)
x
The example above evaluates to 42.
I find that renaming can be done simply with define or let statements:
(define =? =)
(define lr list-ref)
or:
(let ((=? =)
(lr list-ref))
(println (lr '(1 2 3) 2))
(println (=? 1 2))
(println (=? 1 1)))
Output:
3
#f
#t
There seem to be no need for any macro for this purpose.
How do I get this piece of macro to function as intended? -- I'd like to capture p from the lexical environment without having to send it to the macro as an argument.
(define-syntax-rule (fi a b)
(if p a b)) ;--->capture `p` from lexical env
(let ((p #t))
(fi 1 2))
Bonus thanks -- How would I do the same in CL?
In Common Lisp a macro is simply a function that takes as input the list structure of the code and returns a list structure representing the new code.
(defmacro fi (a b)
`(if p ,a ,b))
So if you were to use fi like this:
(let ((p t)) ; Common Lisp uses 't' for truth.
(fi 1 2))
It is as if you had typed:
(let ((p t))
(if p 1 2))
To see how you would get this expansion, imagine fi was a function and you gave it the arguments of 1 and 2.
(fi 1 2) => (if p 1 2)
Then took the list structure it returned and substituted it with the call to fi.
The example you give is simple because the arguments evaluate to themselves. If you had something more complicated like the expressions (* 1 1) and (+ 1 1), the actual list structure is passed in (the value of a is the list (* 1 1), and the value of b is the list (+ 1 1))
(fi (* 1 1) (+ 1 1)) => (if p (* 1 1) (+ 1 1))
You can't capture local bindings with syntax-rules. You can use syntax-case for that, though:
(define-syntax fi
(lambda (stx)
(syntax-case stx ()
((_ a b)
(with-syntax ((p (datum->syntax stx #'p)))
#'(if p a b))))))
However, using datum->syntax to capture identifiers of a fixed name like this is not ideal. If you're using Racket, it's better to use syntax parameters for this.
For Scheme implementations that don't have syntax-case but have explicit renaming, you might write the macro this way:
(define-syntax fi
(er-macro-transformer
(lambda (exp rename compare)
`(,(rename 'if) p ,(cadr exp) ,(caddr exp)))))
Some people find it simpler, but the onus is on you to rename everything that you're not intentionally capturing. In this case, we're explicitly renaming if; for most other macros that use lambda, let, etc., those all must be renamed.