evaluating forms during macro expansion in Racket - scheme

This Common Lisp macro and test function
(defmacro test (body)
`(let ,(mapcar #'(lambda (s)
`(,s ,(char-code (char-downcase (char (symbol-name s) 0)))))
'(a b))
,body))
(test (+ a b))
expands into
(let ((a 97) (b 98))
(+ a b))
and gives 195 when evaluated
Trying to do that in Racket
(define-syntax (test stx)
(syntax-case stx ()
[(_ body)
#`(let #,(map (lambda (x)
(list x
(char->integer (car (string->list (symbol->string x))))))
'(a b))
body)]))
(test (+ a b))
When I run the macroexpander, the macro form expands to:
(let ((a 97) (b 98)) (+ a b))))
which is what I thought I wanted.
But it fails with:
a: unbound identifier in context..
Disabling macro hiding gives a form that ends with:
(#%app:35
call-with-values:35
(lambda:35 ()
(let-values:36 (((a:37) (quote 97)) ((b:37) (quote 98)))
(#%app:38 + (#%top . a) b)))
(print-values:35)))
I don't understand why my nice expansion (let ((a 97) (b 98)) (+ a b)) doesn't work, and I'm puzzled by (#%top .a)... I wonder if it's trying to find a function called "a"?
When I copy the expanded form into the REPL, it works...
I'm grateful for any help!

Racket has hygienic macro. Consider:
(define-syntax-rule (or a b)
(let ([a-val a])
(if a-val a-val b)))
Then:
(let ([a-val 1])
(or #f a-val))
will roughly expand to:
(let ([a-val 1])
(let ([a-val2 #f])
(if a-val2 a-val2 a-val)))
which evaluates to 1. If macro is not hygienic, then it would result in #f, which is considered incorrect.
Notice that a-val is renamed to a-val2 automatically to avoid the collision. That's what happens to your case too.
One way to fix the problem in your case is to give a correct context to the generated identifiers so that the macroexpander understands that they should refer to the same variable.
(define-syntax (test stx)
(syntax-case stx ()
[(_ body)
#`(let #,(map (lambda (x)
(list (datum->syntax stx x) ; <-- change here
(char->integer (car (string->list (symbol->string x))))))
'(a b))
body)]))
(test (+ a b))

As a counterpart Sorawee Porncharoenwase's answer (which is the right answer) I think it's worth while thinking a bit about why your test macro is problematic in CL and why macros which do similar things are outright buggy.
Given your test macro, imagine some user looking at this code:
(let ((a 1) (b 2))
(test (+ a b)))
Well, I don't know about you, but what I would expect to happen is that the a and b inside test are the a and b I've just bound. But that's not the case at all, of course.
Well, perhaps the documentation for test describes in great detail that it binds two variables, and that this is what I should expect. And, of course, there are macros which do just that, and where it's fine:
(defmacro awhen (test &body forms)
`(let ((it ,test))
(when ,it ,#forms)))
And now:
(awhen (find-exploder thing)
(explode it))
And this is all fine, because the documentation for awhen will say that it binds it to the result of the test in its body.
But now consider this or macro stolen from the other answer:
(defmacro vel (a b)
`(let ((a-val ,a))
(if a-val a-val ,b)))
This is a disaster. It 'works', except it doesn't work at all:
> (let ((a-val 3))
(vel nil a-val))
nil
Now that's not just surprising in the way your test macro is: it's wrong.
Instead, you have to write vel like this in CL:
(defmacro vel (a b)
(let ((a-val-name (make-symbol "A-VAL")))
`(let ((,a-val-name ,a))
(if ,a-val-name ,a-val-name ,b))))
(You can of course use gensym instead of make-symbol, and most people do I think.)
And now
> (let ((a-val 3))
(vel nil a-val))
3
as you would expect.
This is all because the CL macro system is unhygenic – it relies on you to ensure that things like names do not clash. In CL you have to go slightly out of your way to write macros which are correct in many cases. The Racket macro system, on the other hand, is hygenic: it will by default ensure that names (and other things) don't clash. In Racket (and Scheme) you have to go out of your way to write macros which are either incorrect or do something slightly unexpected like introducing bindings visible from code which makes use of the macros.
Note that I'm not expressing a preference for either approach to macros: I've spent most of my life writing CL, and I'm very happy with its macro system. More recently I've written more Racket and I'm happy with its macro system as well, although I find it harder to understand.
Finally here is a variant of your macro which is less surprising in use (almost all of the noise in this code is sanity checking which syntax-parse supports in the form of the two #:fail-when clauses):
(define-syntax (with-char-codes stx)
(syntax-parse stx
[(_ (v:id ...) form ...)
#:fail-when (check-duplicate-identifier (syntax->list #'(v ...)))
"duplicate name"
#:fail-when (for/or ([name (syntax->list #'(v ...))])
(and (> (string-length (symbol->string
(syntax->datum name)))
1)
name))
"name too long"
#'(let ([v (char->integer (string-ref (symbol->string 'v) 0))] ...)
form ...)]))
And now
> (with-char-codes (a b)
(+ a b))
195

Related

What is Classifier and Transformer in MIT Scheme?

In the following scheme code, accumulate does right-fold. When I tried to run using mit scheme. I ran into following error:
Transformer may not be used as an expression: #[classifier-item 13]
Classifier may not be used as an expression: #[classifier-item 12]
I google searched but didn't find useful information. Is it related a macro?
; This function is copied from SICP chapter 2
(define (accumulate op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence)))))
; works as expected
(accumulate
(lambda (x y) (or x y)) ; replace or with and also works
#f
'(#t #f #t #f #f)
))
; does not work
; error: Classifier may not be used as an expression: #[classifier-item 12]
(accumulate
or
#f
'(#t #f #t #f #f)
))
; does not work
; error: Transformer may not be used as an expression: #[classifier-item 13]
(accumulate
and
#f
'(#t #f #t #f #f)
))
Macros can be passed around in some languages, but not in Scheme and Common Lisp. The reason is that macros should be able to be expanded ahead of time. eg.
(define (cmp a b)
(cond ((< a b) -1)
((> a b) 1)
(else 0)))
Now a compiling Scheme will expand each node recursively replacing it with the expansion until it is no change:
(define (cmp a b)
(if (< a b)
(begin -1)
(cond ((> a b) 1)
(else 0))))
(define (cmp a b)
(if (< a b)
-1
(cond ((> a b) 1)
(else 0))))
(define (cmp a b)
(if (< a b)
-1
(if (> a b)
(begin 1)
(cond (else 0)))))
(define (cmp a b)
(if (< a b)
-1
(if (> a b)
1
(cond (else 0)))))
; end result
(define (cmp a b)
(if (< a b)
-1
(if (> a b)
1
0)))
From this point of cond doesn't need to exist in the underlying language at all since you'll never ever use it, but how would this have to be implemented to work:
(define (test syntax a b)
(syntax a b))
(test or #f #t)
For this to work the underlying language needs to know what or is even after expansion since syntax would need to be bound to or and then the transformation can happen. But when the code runs the macro expansion has already happened and in most implementations you would see something indicating that or is an unbound variable. It seems like MIT Scheme has added error checking for top level syntax syntax that will fire an error if you don't override it. Eg. if you add this you will not see any problems whatsoever:
(define (or a b) (if a a b))
(define (and a b) (if a b #f))
Now after those lines any reference to and and or are not the syntax, but these procedures. There are no reserved words in Scheme so if you do something crazy, like defining define you just cannot use it for the rest f that scope:
(define define display) ; defiens define as a top level variable
(define define) ; prints the representation of the function display
(define test 10) ; fail since test is an undefined variable so it cannot be displayed.
I created a interpreted lisp with macros that actually could be passed, but it isn't very useful and the chances of optimization is greatly reduced.
Yes it's related to the macros / special forms like and and or.
You can make it work simply by wrapping them as lambdas, (accumulate (lambda (a b) (or a b)) ...) -- the results will be correct but of course there won't be any short-circuiting then. The op is a function and functions receive their arguments already evaluated.
Either hide the arguments behind lambdas ((lambda () ...)) and evaluate them manually as needed, or define specific versions each for each macro op, like
(define (accumulate-or initial sequence)
(if (null? sequence)
initial
(or (car sequence)
(accumulate-or initial (cdr sequence)))))
Here sequence will still be evaluated in full before the call to accumulate-or, but at least the accumulate-or won't be working through it even after the result is already known.
If sequence contains some results of heavy computations which you want to avoid in case they aren't needed, look into using "lazy sequences" for that.

Standard way to handle quoted symbol in lisp macros in Scheme

For some code I was working I've needed to handle 'x inside macro. What is standard way of handling those values?
I have code like this:
(define (quoted-symbol? x)
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
(define-macro (test x)
(if (quoted-symbol? x)
`(begin
(display ',(cadr x))
(newline))))
(test 'hello) ;; 'hello will be expanded into list (quote hello)
Is this how this should be handled, or is just in macro you don't use quoted symbols?
NOTE: I'm not asking about hygienic macros (I'm asking about real lisp macros), so please no answers with hygienic macros.
EDIT:
My macro works correctly in Guile and BiwaScheme and in my own scheme like lisp in JavaScript. Here is better example:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(define (example arg)
(list arg (test 'world)))
(example 'hello)
the question was not about display, but about (cadr x).
EDIT2: You've asked so here you go, my macro:
(define-macro (--> expr . code)
"Helper macro that simplify calling methods on objects. It work with chaining
usage: (--> ($ \"body\")
(css \"color\" \"red\")
(on \"click\" (lambda () (print \"click\"))))
(--> document (querySelectorAll \"div\"))
(--> (fetch \"https://jcubic.pl\") (text) (match /<title>([^<]+)<\/title>/) 1)
(--> document (querySelectorAll \".cmd-prompt\") 0 \"innerText\")"
(let ((obj (gensym)))
`(let* ((,obj ,(if (and (symbol? expr) (not (null? (match /\./ (symbol->string expr)))))
`(.. ,expr)
`,expr)))
,#(map (lambda (code)
(let ((name (gensym))
(value (gensym)))
`(let* ((,name ,(cond ((quoted-symbol? code) (symbol->string (cadr code)))
((pair? code) (symbol->string (car code)))
(true code)))
(,value (. ,obj ,name)))
,(if (and (pair? code) (not (quoted-symbol? code)))
`(set! ,obj (,value ,#(cdr code)))
`(set! ,obj ,value)))))
code)
,obj)))
;; ---------------------------------------------------------------------------------------
(define (quoted-symbol? x)
"(quoted-symbol? code)
Helper function that test if value is quoted symbol. To be used in macros
that pass literal code that is transformed by parser.
usage:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(list 'hello (test 'world))"
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
the macro is used in my scheme like lisp in JavaScript, like the doc string suggest:
(--> document (querySelectorAll ".class") 0 "innerText")
I want to support:
(--> document (querySelectorAll ".class") 0 'innerText)
The code can be tested online at: https://jcubic.github.io/lips/ (You need to copy/paste the code since current version allow only method calls).
To get expansion you can use
(pprint (macroexpand (--> document (querySelector "x"))))
if it don't work (don't expand) it mean that macro is broken somehow.
dot is build in function that get property of an object and .. macro:
(define-macro (.. expr)
"(.. foo.bar.baz)
Macro that gets value from nested object where argument is comma separated symbol"
(if (not (symbol? expr))
expr
(let ((parts (split "." (symbol->string expr))))
(if (single parts)
expr
`(. ,(string->symbol (car parts)) ,#(cdr parts))))))
that can be use to get nested property like (.. document.body.innerHTML)
Scheme doesn't have "real lisp macros". Some implementations has something similar, but the forms have different names and uses. They are not portable at all.
The standard way of handling 'x is to handle it like an expression that gets evaluated in the expansion. Eg.
(define var 'x)
(test 'x)
(test var)
The two test forms should amount to the same even though the macro test gets (quote x) in the first and the symbol var in the second. At the time of the expansion var does not exist since the implementation can expand all the macros before starting.
You implementation of test will not work. Eg. the display might be run one or twice and then each time you call a procedure that uses it it will gfail since the expansion is the undefined value and it might not be fit for evaluation. eg.
(define (example arg)
(list arg (test 'w)))
When this is defined you get 'w or (quote w) printed with a newline and then the procedure it tries to store is:
(define (example arg)
(list arg #<undefined>))
Note that what constitutes the undefined value is chosen by the implementaion, but I know for sure that in many implementaions you cannot evaluate #<undefined>.

Does call/cc in Scheme the same thing with yield in Python and JavaScript?

Does call/cc in Scheme the same thing with yield in Python and JavaScript?
I am not clear about generators. In my opinion, yield gives a language the ability to generate iterators without pain. But I am not sure whether I am right.
Does call/cc in Scheme has something related to yield in other languages? If so, are they the same thing, or what is the difference?
Thanks!
call/cc is a much more general language feature than generators. Thus you can make generators with call/cc, but you cannot make call/cc with generators.
If you have a program that computes values and use those values in other places its basically a step machine.. One might think of it as a program that have one function for each step and a continuation for the rest of the steps. Thus:
(+ (* 3 4) (* 5 6))
Can be interpreted as:
((lambda (k)
(k* 3 4 (lambda (v34)
(k* 5 6 (lambda (v56)
(k+ v34 v56 k)))))
halt)
The k-prefix just indicate that it's a CPS-version of the primitives. Thus they call the last argument as a function with the result. Notice also that the order of evaluation, which is not defined in Scheme, is in fact chosen in this rewrite. In this beautiful language call/cc is just this:
(define (kcall/cc kfn k)
(kfn (lambda (value ignored-continuation)
(k value))
k))
So when you do:
(+ (* 3 4) (call/cc (lambda (exit) (* 5 (exit 6)))))
; ==> 18
Under the hood this happens:
((lambda (k)
(k* 3 4 (lambda (v34)
(kcall/cc (lambda (exit k)
(exit 6 (lambda (v6)
(k* 5 v6 k)))
k))))
halt)
By using the substitution we can prove that this actually does exactly as intended. Since the exit function is invoked the original continuation is never called and thus the computation cancelled. In contrast to call/cc giving us this continuation that doesn't seem obvious it's no magic in CPS. Thus much of the magic of call/cc is in the compiler stage.
(define (make-generator procedure)
(define last-return values)
(define last-value #f)
(define (last-continuation _)
(let ((result (procedure yield)))
(last-return result)))
(define (yield value)
(call/cc (lambda (continuation)
(set! last-continuation continuation)
(set! last-value value)
(last-return value))))
(lambda args
(call/cc (lambda (return)
(set! last-return return)
(if (null? args)
(last-continuation last-value)
(apply last-continuation args))))))
(define test
(make-generator
(lambda (collect)
(collect 1)
(collect 5)
(collect 10)
#f)))
(test) ; ==> 1
(test) ; ==> 5
(test) ; ==> 10
(test) ; ==> #f (procedure finished)
One might make a macro to make the syntax more similar, but it's just sugar on top of this.
For more examples I love Matt Mights page with lots of examples on how to use continuations.
Here's code to define a generator:
(define-syntax define-generator
(lambda (x)
(syntax-case x (lambda)
((stx name (lambda formals e0 e1 ...))
(with-syntax ((yield (datum->syntax (syntax stx) 'yield)))
(syntax (define name
(lambda formals
(let ((resume #f) (return #f))
(define yield
(lambda args
(call-with-current-continuation
(lambda (cont)
(set! resume cont)
(apply return args)))))
(lambda ()
(call-with-current-continuation
(lambda (cont)
(set! return cont)
(cond (resume (resume))
(else (let () e0 e1 ...)
(error 'name "unexpected return"))))))))))))
((stx (name . formals) e0 e1 ...)
(syntax (stx name (lambda formals e0 e1 ...)))))))
There are examples of the use of generators at my blog. Generators use call-with-current-continuation, in a manner similar to yield in Python, but are much more general.
You can implement generators with call/cc. Here is an example:
coroutines.ss
They work in a similar way to python and ECMAScript generators.

Is there a Scheme toString method for a procedure?

I want to be able to take a procedure and see what it looks like. Is this possible?
For example, let's say I have:
(define (some-func x)
(+ x 1))
What I want to do is apply some amazing function (say, stringify) to some-func and be able to look at its guts.
\> (stringify some-func)
"(lambda (x) (+ x 1))"
I haven't found any Racket libraries that do it. Can it be done?!
In R6RS, there is no sure way to determine if two procedures are equivalent; even an expression like (let ((p (lambda () 42))) (eqv? p p)) is not guaranteed to be true.
R7RS addresses that by using the concept of "location tags", where each lambda expression generates a unique location tag. Then eqv? works for procedures by comparing location tags: thus, (let ((p (lambda () 42))) (eqv? p p)) is true, and (eqv? (lambda () 42) (lambda () 42)) is false.
There is no reliable way to get the source of a procedure (many implementations macro-expand and compile the procedures, discarding the original source), and even if you could, you could not use it to compare if two procedures are "equal", because of closures (and that two procedures could have the same "source" but have their free variables bound to different things). For example, consider the two expressions (let ((x 1)) (lambda () x)) and (let ((x 2)) (lambda () x)). They have the same "source", but nobody in their right mind would claim that they are equivalent in any way.
Note, you could easily implement a define alternative to keep the source around. You don't avoid the lexical issues but, modulo that, you've got something with limited use.
(define name->source-mapping '())
(define (name->source name)
(cond ((assq name name->source-mapping) => cdr)
(else #f)))
(define (name->source-extend name source)
(set! name->source-mapping (cons (cons name source) name->source-mapping))
(define-syntax define-with-source
((_ (name args ...) body1 body2 ...)
(define name
(begin (name->source-mapping-extend 'name '(lambda (args ...) body1 body2 ...))
name->source-mapping))
(lambda (args ...) body1 body2 ...)))))
[Above does not replace (define name value) syntax; consider the above an example only.]

"cond","and" and "or" in Scheme

I'm reading The Little Schemer. And thanks to my broken English, I was confused by this paragraph:
(cond ... ) also has the property of not considering all of its
arguments. Because of this property, however, neither (and ... ) nor
(or ... ) can be defined as functions in terms of (cond ... ), though
both (and ... ) and (or ... ) can be expressed as abbreviations of
(cond ... )-expressions:
(and a b) = (cond (a b) (else #f)
and
(or a b) = (cond (a #t) (else (b))
If I understand it correctly, it says (and ...) and (or ...) can be replaced by a (cond ...) expression, but cannot be defined as a function that contains (cond ...). Why is it so? Does it have anything to do with the variant arguments? Thanks.
p.s. I did some searching but only found that (cond ...) ignores the expressions when one of its conditions evaluate to #f.
Imagine you wrote if as a function/procedure rather than a user defined macro/syntax:
;; makes if in terms of cond
(define (my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))
;; example that works
(define (atom? x)
(my-if (not (pair? x))
#t
#f))
;; example that won't work
;; peano arithemtic
(define (add a b)
(my-if (zero? a)
b
(add (- a 1) (+ b 1))))
The problem with my-if is that as a procedure every argument gets evaluated before the procedure body gets executed. thus in atom? the parts (not (pair? x)), #t and #f were evaluated before the body of my-if gets executed.
For the last example means (add (- a 1) (+ b 1)) gets evaluated regardless of what a is, even when a is zero, so the procedure will never end.
You can make your own if with syntax:
(define-syntax my-if
(syntax-rules ()
((my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))))
Now, how you read this is the first part is a template where the predicate consequent and alternative represent unevaluated expressions. It's replaced with the other just reusing the expressions so that:
(my-if (check-something) (display 10) (display 20))
would be replaced with this:
(cond ((check-something) (display 10))
(else (display 20)))
With the procedure version of my-if both 10 and 20 would have been printed. This is how and and or is implemented as well.
You cannot define cond or and or or or if as functions because functions evaluate all their arguments. (You could define some of them as macros).
Read also the famous SICP and Lisp In Small Pieces (original in French).

Resources