Background:
I intend to generate debug messages for the code I'm developing. I wrote a macro to avoid writing the logging calls in each function. I know this limits the ability to generate more customized debug messages, but in return it isolates logging from code. And that is what I'm aiming for. This macro approach has other drawbacks too, for example it limits creation of function bindings to this macro only, but I think I can live with that.
Following is the definition of the macro and an example demonstrating its usage.
(define-syntax (define-func stx)
(syntax-case stx ()
[(define-func (func-name args ...) body1 body2 ...)
(if (and (identifier? #'func-name)
(andmap symbol? (syntax->datum #'(args ...))))
(syntax (define (func-name args ...)
(log-debug (format "Function-name ~a:" (syntax-e #'func-name)) (list args ...))
body1
body2 ...))
(raise-syntax-error 'define-func "not an identifier" stx))]
[else (raise-syntax-error 'define-func "bad syntax" stx)]))
(define-func (last l)
(cond [(null? l) null]
[(null? (rest l)) (first l)]
[else (last (rest l))]))
(define-func (main)
(last (list 1 2 3 4 5 6 7 8 9))
(logger))
log-debug and logger are defined in separate module
The output produced is somewhat like following:
Function-name last:
args:
:-> (7 8 9)
Function-name last:
args:
:-> (8 9)
Function-name last:
args:
:-> (9)
Now I want to make it more readable. By readability I mean to provide some kind of indentation so that the person reading the log could make sense of call flow. For example something like following:
Function-name last:
args:
:-> (7 8 9)
Function-name last:
args:
:-> (8 9)
Function-name last:
args:
:-> (9)
It is easier to figure out who called whom and so forth. I have an idea that can do this. It involves a variable that keeps track of indentation then after logging the function name I will increase the indent and after evaluation of body and before returning the value decrements the value. Something like following:
(define indent 0)
(define-syntax (define-func stx)
(syntax-case stx ()
[ (... ...)
(...
(log-debug ...)
(increment indent)
(let [(retval (body1 body2 ...)]
(decrease indent)
retval))]))
increment and decrease increases and decreases indentation respectively.
Problem:
It works even for function that returns void. I'm not sure whether its the correct behavior. In racket void is a special value, but I'm not sure that creating a binding to void is right way to go.
Is there any better way to achieve the same? If not are there any problems in this design? I'm open to any idea/change as long as they keep the logging and code separate.
thanks for the help!
I have several suggestions for you:
It's probably better to use a parameter instead of a variable, for "global" stuff like your indentation level, since the original value is restored for you at the end of the parameterize expression.
All those raise-syntax-error checks you have in your macro are totally superfluous: syntax-case already provides guards (also known as fenders) that allow you to do any validation of macro "arguments" necessary:
(define-syntax (define-func stx)
(syntax-case stx ()
[(_ (func-name args ...) body1 body2 ...)
(andmap identifier? (syntax->list #'(func-name args ...)))
#'(define (func-name args ...)
(log-debug (format "Function-name ~a:" 'func-name)
(list args ...))
body1
body2 ...)]))
I've also fixed up your code in several places, as you can see above:
I used (_ ...) instead of (define-func ...), since in syntax-case (unlike syntax-rules), the latter will actually bind a pattern variable called define-func, which will affect any recursive macro calls you may want to do (I'll grant that you don't have one here, but it's a good habit to have anyway).
Rather than completely flatten the #'(args ...) in the guard, I just turned it into a list of syntax objects so you can test using identifier?. This is more intention-revealing than testing using symbol?, and allows us to also test func-name in the same expression.
You don't need to use (syntax-e #'func-name) inside the expanded code! Just quote it.
Related
I am using syntax transformers to define macros in Racket. I want to create some helper functions to help me manipulate the syntax. However, the functions I defined outside the syntax transformer are not available inside the syntax transformer. For example, in the following code
(define (my-function x) (+ x 1))
(define-syntax my-macro
(lambda (stx)
(datum->syntax stx (my-function (cadr (syntax->datum stx))))))
I got the error "my-function: reference to an unbound identifier at phase: 1; the transformer environment".
After some searching, I am able to write the following code so that my-function is available inside the syntax transformer.
(begin-for-syntax
(define (my-function x) (+ x 1)))
(provide (for-syntax my-function))
(define-syntax my-macro
(lambda (stx)
(datum->syntax stx (my-function (cadr (syntax->datum stx))))))
But the problem is, my-function is not available outside the syntax transformer this time. Sometimes I want to check those helper functions in ordinary code, so I need to be able to call it from both inside and outside the syntax transformer, just like the function cadr. How can I achieve that?
I know my question has something to do with Racket's syntax model, in particular the concept of "phase level", but I never really understand it. If you could provide some easy-to-follow tutorials explaining it I would even be more grateful.
A common way is to define your function that you want to share across phases in another (sub)module. Then, require it twice.
#lang racket
(module common racket
(provide my-function)
(define (my-function x) (+ x 1)))
(require 'common
(for-syntax 'common))
(define-syntax my-macro
(lambda (stx)
(datum->syntax stx (my-function (cadr (syntax->datum stx))))))
(my-function 1)
(my-macro 123)
make-variable-transformer (or make-set!-transformer, as it is called in Racket) can work with identifiers hardcoded in the literal list of the macro definition. The following example with set! invariably comes up when variable transformers are mentioned:
(make-variable-transformer
(λ (stx)
(syntax-case stx (set!)
((set! id _) ...)
(id ...))))
This is nice and all, useful for transparently integrating foreign structures with primitive operations known ahead of time, and it's a bonus that it can work through identifier syntax and rename transformers.
But what I'm wondering is if it's possible to work with syntax dynamically like:
(let-syntax ((# (make-variable-transformer
(λ (stx)
(syntax-case stx ()
((v # i) (vector? #'v) #'(vector-ref v i)))))))
(#(0 1 2) # 1))
=> 1
This doesn't work because the macro call doesn't match the template as syntax-case expects # to be in the initial position since there's no v in the literal list (and it probably assigns # to v pattern variable).
In short: Is it possible to write a syntax transformer that can accomplish this without reader extensions or overriding application, perhaps through a metamacro that rewrites the literal token list of an inner syntax-case (à la Petrofsky extraction)?
NB: The vector example itself is unimportant and I'm not interested in alternative solutions to this exact use-case.
since there's no v in the literal list (and it probably assigns # to v pattern variable).
Not really. set! is a special case that the macro expander handles specifically to make it cooperate with make-variable-transformer. But for other literals, they will fail. E.g.,
(let-syntax ((# (make-variable-transformer
(λ (stx)
(syntax-case stx (v)
((v # i) #'1))))))
(v # 1))
fails with v: unbound identifier.
The second issue with your above code is the side condition (vector? #'v). #'v is a syntax object, so (vector? #'v) will always result in #f. It's unclear what is the right behavior. For example, do you intend for:
(define v (vector 1 2 3))
(v # 1)
to work? If so, a compile-time side condition would be inappropriate, because it's not known if v is a vector at compile-time.
For your main question, the answer is no. It's not possible under the constraints that you imposed. The expansion steps are detailed here, and none of the steps looks beyond the head of the list.
But if we don't care about the constraints. I.e., overriding #%app is OK. It could work.
An issue that you need to think about is, suppose you have (a b c) where b is your kind of macro and a is a regular macro. Who should get the control first? If a should get the control first, you can override #%app to implement this kind of macro. Here's my quick implementation.
#lang racket
(require syntax/parse/define
(only-in racket [#%app racket:#%app])
(for-syntax syntax/apply-transformer))
(begin-for-syntax
(struct my-transformer (t)))
(define-syntax-parser #%app
[(_ x ...)
(define transformer
(for/first ([operand (attribute x)]
#:when (and (identifier? operand)
(my-transformer?
(syntax-local-value operand (λ () #f)))))
(syntax-local-value operand)))
(cond
[transformer (local-apply-transformer
(my-transformer-t transformer)
#'(x ...)
'expression)]
[else #'(racket:#%app x ...)])])
(define-syntax #
(my-transformer
(syntax-parser
[(v _ i) #'(vector-ref v i)])))
(define v (vector 42 1337 1729))
(v # 1) ;=> 1337
Finally, you can always override #%module-begin and simulate the macro expander. It's an overkill solution, but could be appropriate if you want more advanced features, like allowing users to customize precedence so that b is expanded before a.
I'm in a process of implementing Hygienic macros in my Scheme implementation, I've just implemented syntax-rules, but I have this code:
(define odd?
(syntax-rules ()
((_ x) (not (even? x)))))
what should be the difference between that and this:
(define-syntax odd?
(syntax-rules ()
((_ x) (not (even? x)))))
from what I understand syntax-rules just return syntax transformer, why you can't just use define to assign that to symbol? Why I need to use define-syntax? What extra stuff that expression do?
Should first also work in scheme? Or only the second one?
Also what is the difference between let vs let-syntax and letrec vs letrec-syntax. Should (define|let|letrec)-syntax just typecheck if the value is syntax transformer?
EDIT:
I have this implementation, still using lisp macros:
;; -----------------------------------------------------------------------------
(define-macro (let-syntax vars . body)
`(let ,vars
,#(map (lambda (rule)
`(typecheck "let-syntax" ,(car rule) "syntax"))
vars)
,#body))
;; -----------------------------------------------------------------------------
(define-macro (letrec-syntax vars . body)
`(letrec ,vars
,#(map (lambda (rule)
`(typecheck "letrec-syntax" ,(car rule) "syntax"))
vars)
,#body))
;; -----------------------------------------------------------------------------
(define-macro (define-syntax name expr)
(let ((expr-name (gensym)))
`(define ,name
(let ((,expr-name ,expr))
(typecheck "define-syntax" ,expr-name "syntax")
,expr-name))))
This this code correct?
Should this code works?
(let ((let (lambda (x) x)))
(let-syntax ((odd? (syntax-rules ()
((_ x) (not (even? x))))))
(odd? 11)))
This question seems to imply some deep confusion about macros.
Let's imagine a language where syntax-rules returns some syntax transformer function (I am not sure this has to be true in RnRS Scheme, it is true in Racket I think), and where let and let-syntax were the same.
So let's write this function:
(define (f v)
(let ([g v])
(g e (i 10)
(if (= i 0)
i
(e (- i 1))))))
Which we can turn into this, of course:
(define (f v n)
(v e (i n)
(if (<= i 0)
i
(e (- i 1)))))
And I will tell you in addition that there is no binding for e or i in the environment.
What is the interpreter meant to do with this definition? Could it compile it? Could it safely infer that i can't possibly make any sense since it is used as a function and then as a number? Can it safely do anything at all?
The answer is that no, it can't. Until it knows what the argument to the function is it can't do anything. And this means that each time f is called it has to make that decision again. In particular, v might be:
(syntax-rules ()
[(_ name (var init) form ...)
(letrec ([name (λ (var)
form ...)])
(name init))]))
Under which the definition of f does make some kind of sense.
And things get worse: much worse. How about this?
(define (f v1 v2 n)
(let ([v v1])
(v e (i n)
...
(set! v (if (eq? v v1) v2 v1))
...)))
What this means is that a system like this wouldn't know what the code it was meant to interpret meant until, the moment it was interpreting it, or even after that point, as you can see from the second function above.
So instead of this horror, Lisps do something sane: they divide the process of evaluating bits of code into phases where each phase happens, conceptually, before the next one.
Here's a sequence for some imagined Lisp (this is kind of close to what CL does, since most of my knowledge is of that, but it is not intended to represent any particular system):
there's a phase where the code is turned from some sequence of characters to some object, possibly with the assistance of user-defined code;
there's a phase where that object is rewritten into some other object by user- and system-defined code (macros) – the result of this phase is something which is expressed in terms of functions and some small number of primitive special things, traditionally called 'special forms' which are known to the processes of stage 3 and 4;
there may be a phase where the object from phase 2 is compiled, and that phase may involve another set of user-defined macros (compiler macros);
there is a phase where the resulting code is evaluated.
And for each unit of code these phases happen in order, each phase completes before the next one begins.
This means that each phase in which the user can intervene needs its own set of defining and binding forms: it needs to be possible to say that 'this thing controls what happens at phase 2' for instance.
That's what define-syntax, let-syntax &c do: they say that 'these bindings and definitions control what happens at phase 2'. You can't, for instance, use define or let to do that, because at phase 2, these operations don't yet have meaning: they gain meaning (possibly by themselves being macros which expand to some primitive thing) only at phase 3. At phase 2 they are just bits of syntax which the macro is ingesting and spitting out.
Suppose I have some module with non-trivial define "override" in Racket. That "override" collects information about the procedure-body and stores it into a map (during the compilation phase). Now I need to use the collected information during the runtime phase. The straightforward aproach doesn`t seem to work:
#lang racket
(require (for-syntax racket))
(define-for-syntax map-that-should-be-used-in-phase-0 (make-hash))
(define-for-syntax (fill-in-useful-information n) (hash-set! map-that-should-be-used-in-phase-0 n n))
; Suppose that some useful information is collected here and stored into a map
(define-syntax (fill-in-map stx)
(begin
(fill-in-useful-information 1)
(fill-in-useful-information 2)
(syntax/loc stx (displayln "OK, the map is filled, but I cannot see it here"))))
(define-syntax (print-that-map stx)
(syntax/loc stx (displayln map-that-should-be-used-in-phase-0))) ; <-- This can not be compiled
(fill-in-map)
(print-that-map)
Can I do it in Racket? If yes then how? Any hints will be greately appreciated!
An identifier referencing a variable cannot be compiled, but the value it refers to can, as long as it's one of the built-in data structures provided by Racket, and as long as it's immutable.
You can stick a hash table value into a syntax object using quasisyntax and unsyntax.
> (quasisyntax (foo #,(hash 'a 4 'b 16)))
#<syntax:5:15 (foo #hash((a . 4) (b . 16)))>
You can do the same thing to communicate one-way from compile-time to run-time.
(define-for-syntax (hash->immutable-hash hsh)
(make-immutable-hash (hash->list hsh)))
(define-syntax (print-that-map stx)
(quasisyntax/loc stx (displayln #,(hash->immutable-hash map-that-should-be-used-in-phase-0))))
What's the simplest way to define a capturing macro using define-syntax or define-syntax-rule in Racket?
As a concrete example, here's the trivial aif in a CL-style macro system.
(defmacro aif (test if-true &optional if-false)
`(let ((it ,test))
(if it ,if-true ,if-false)))
The idea is that it will be bound to the result of test in the if-true and if-false clauses. The naive transliteration (minus optional alternative) is
(define-syntax-rule (aif test if-true if-false)
(let ((it test))
(if it if-true if-false)))
which evaluates without complaint, but errors if you try to use it in the clauses:
> (aif "Something" (displayln it) (displayln "Nope")))
reference to undefined identifier: it
The anaphora egg implements aif as
(define-syntax aif
(ir-macro-transformer
(lambda (form inject compare?)
(let ((it (inject 'it)))
(let ((test (cadr form))
(consequent (caddr form))
(alternative (cdddr form)))
(if (null? alternative)
`(let ((,it ,test))
(if ,it ,consequent))
`(let ((,it ,test))
(if ,it ,consequent ,(car alternative)))))))))
but Racket doesn't seem to have ir-macro-transformer defined or documented.
Racket macros are designed to avoid capture by default. When you use define-syntax-rule it will respect lexical scope.
When you want to "break hygiene" intentionally, traditionally in Scheme you have to use syntax-case and (carefully) use datum->syntax.
But in Racket the easiest and safest way to do "anaphoric" macros is with a syntax parameter and the simple define-syntax-rule.
For example:
(require racket/stxparam)
(define-syntax-parameter it
(lambda (stx)
(raise-syntax-error (syntax-e stx) "can only be used inside aif")))
(define-syntax-rule (aif condition true-expr false-expr)
(let ([tmp condition])
(if tmp
(syntax-parameterize ([it (make-rename-transformer #'tmp)])
true-expr)
false-expr)))
I wrote about syntax parameters here and also you should read Eli Barzilay's Dirty Looking Hygiene blog post and Keeping it Clean with Syntax Parameters paper (PDF).
See Greg Hendershott's macro tutorial. This section uses anaphoric if as example:
http://www.greghendershott.com/fear-of-macros/Syntax_parameters.html
Although the answer above is the accepted way to implement aif in the Racket community, it has severe flaws. Specifically, you can shadow it by defining a local variable named it.
(let ((it 'gets-in-the-way))
(aif 'what-i-intended
(display it)))
The above would display gets-in-the-way instead of what-i-intended, even though aif is defining its own variable named it. The outer let form renders aif's inner let definition invisible. This is what the Scheme community wants to happen. In fact, they want you to write code that behaves like this so badly, that they voted to have my original answer deleted when I wouldn't concede that their way was better.
There is no bug-free way to write capturing macros in Scheme. The closest you can come is to walk down the syntax tree that may contain variables you want to capture and explicitly strip the scoping information that they contain, replacing it with new scoping information that forces them to refer to your local versions of those variables. I wrote three "for-syntax" functions and a macro to help with this:
(begin-for-syntax
(define (contains? atom stx-list)
(syntax-case stx-list ()
(() #f)
((var . rest-vars)
(if (eq? (syntax->datum #'var)
(syntax->datum atom))
#t
(contains? atom #'rest-vars)))))
(define (strip stx vars hd)
(if (contains? hd vars)
(datum->syntax stx
(syntax->datum hd))
hd))
(define (capture stx vars body)
(syntax-case body ()
(() #'())
(((subform . tl) . rest)
#`(#,(capture stx vars #'(subform . tl)) . #,(capture stx vars #'rest)))
((hd . tl)
#`(#,(strip stx vars #'hd) . #,(capture stx vars #'tl)))
(tl (strip stx vars #'tl)))))
(define-syntax capture-vars
(λ (stx)
(syntax-case stx ()
((_ (vars ...) . body)
#`(begin . #,(capture #'(vars ...) #'(vars ...) #'body))))))
That gives you the capture-vars macro, which allows you to explicitly name the variables from the body you'd like to capture. aif can then be written like this:
(define-syntax aif
(syntax-rules ()
((_ something true false)
(capture-vars (it)
(let ((it something))
(if it true false))))
((_ something true)
(aif something true (void)))))
Note that the aif I have defined works like regular Scheme's if in that the else-clause is optional.
Unlike the answer above, it is truly captured. It's not merely a global variable:
(let ((it 'gets-in-the-way))
(aif 'what-i-intended
(display it)))
The inadequacy of just using a single call to datum->syntax
Some people think that all you have to do to create a capturing macro is use datum->syntax on one of the top forms passed to your macro, like this:
(define-syntax aif
(λ (stx)
(syntax-case stx ()
((_ expr true-expr false-expr)
(with-syntax
((it (datum->syntax #'expr 'it)))
#'(let ((it expr))
(if it true-expr false-expr))))
((_ expr true-expr)
#'(aif expr true-expr (void))))))
Just using datum->syntax is only a 90% solution to writing capturing macros. It will work in most cases, but break in some cases, specifically if you incorporate a capturing macro written this way in another macro. The above macro will only capture it if the expr comes from the same scope as the true-expr. If they come from different scopes (this can happen merely by wrapping the user's expr in a form generated by your macro), then the it in true-expr will not be captured and you'll be left asking yourself "WTF won't it capture?"
You may be tempted to quick-fix this by using (datum->syntax #'true-expr 'it) instead of (datum->syntax #'expr 'it). In fact this makes the problem worse, since now you won't be able to use aif to define acond:
(define-syntax acond
(syntax-rules (else)
((_) (void))
((_ (condition . body) (else . else-body))
(aif condition (begin . body) (begin . else-body)))
((_ (condition . body) . rest)
(aif condition (begin . body) (acond . rest)))))
If aif is defined using the capture-vars macro, the above will work as expected. But if it's defined by using datum->syntax on the true-expr, the the addition of begin to the bodies will result in it being visible in the scope of acond's macro definition instead of the code that invoked acond.
The impossibility of really writing a capturing macro in Racket
This example was brought to my attention, and demonstrates why you just can't write a real capturing macro in Scheme:
(define-syntax alias-it
(syntax-rules ()
((_ new-it . body)
(let ((it new-it)) . body))))
(aif (+ 1 2) (alias-it foo ...))
capture-vars cannot capture the it in alias-it's macroexpansion, because it won't be on the AST until after aif is finished expanding.
It is not possible at all to fix this problem, because the macro definition of alias-it is most probably not visible from the scope of aif's macro definition. So when you attempt to expand it within aif, perhaps by using expand, alias-it will be treated as a function. Testing shows that the lexical information attached to alias-it does not cause it to be recognized as a macro for a macro definition written out of scope from alias-it.
Some would argue that this shows why the syntax-parameter solution is the superior solution, but perhaps what it really shows is why writing your code in Common Lisp is the superior solution.