[racket] anamorphic macros
[Aggregate reply, since you got replies from all over the place...]
On Sun, Jan 18, 2015 at 7:49 PM, Peter Samarin <petrsamarin at gmail.com> wrote:
> Hi all,
>
> I was working through chapter 16 of "land of lisp" and there is (at
> that point) a buggy split macro defined like this:
>
> (defmacro split (val yes no)
> `(if ,val
> (let ((head (car ,val))
> (tail (cdr ,val)))
> ,yes)
> ,no))
>
> Here is my version of the equivalently buggy Racket counterpart:
> [...]
As Neil said, using `eval' is almost always a bad idea. A genral rule
of thumb is that if you're not sure whether it's a bad idea, then it is.
(Seriously.)
If you really want an unhygienic Racket translation of that macro, then:
(require compatibility/defmacro)
and then you can use the above macro definition as-is. But it is
unhygienic, which is still a problem.
On Sun, Jan 18, 2015 at 8:04 PM, Jon Zeppieri <zeppieri at gmail.com> wrote:
> Breaking hygiene with `syntax-case`:
>
> (define-syntax (split stx)
> (syntax-case stx ()
> ([split val yes no]
> (let ([head-stx (datum->syntax stx 'head)]
> [tail-stx (datum->syntax stx 'tail)])
> ...))))
Yes, you can do that -- it's kind of similar to what the compatipility
`defmacro' will do for you -- but it's just as bad.
The thing is that Racket has *identifiers*, whereas in Lisp you have
plain symbols. The difference is that an identifier is kind of like a
symbol + its lexical scope. If you're coming from common lisp, then you
can think about it as if each newly nested scope has symbols in a new
(anonymous-ish) package. Keeping that in mind, read
http://barzilay.org/misc/stxparam.pdf to see what's wrong with
unhygienic macros.
On Sun, Jan 18, 2015 at 8:31 PM, Neil Van Dyke <neil at neilvandyke.org> wrote:
> * Just doing it as a procedure that takes closures for the `yes`and
> `no` arguments (with the `yes` one taking arguments for `head` and
> `tail`).
That works -- it's often a way to keep most of the functionality in a
function, and then have a macro that deals with the syntax.
> * Doing it as syntax, but having the user of the extension define the
> . (This might seem overkill, but maybe that's because that's
> because the original macro is already overkill.)
Assuming that you're talking about having the user of the macro provide
the identifiers -- it's a common sentiment, but it doesn't work either.
It breaks when you want to use macros in macros (which is exactly where
the unhygienic solutions break too; see the above PDF for an
explanation).
On Sun, Jan 18, 2015 at 8:19 PM, J. Ian Johnson <ianj at ccs.neu.edu> wrote:
> You're better off defining head and tail as syntax parameters and
> defining your macro with the more hygienic syntax-parameterize using
> rename transformers. [...]
That's the best approach, but since it's still looks as a confusing
issue as ever, it's worth to make this explicit and provide the bits
that Ian skipped:
#lang racket
(require racket/stxparam)
(define-syntax-parameter head
(lambda (s) (raise-syntax-error 'head "can only be used in a `split'" s)))
(define-syntax-parameter tail
(lambda (s) (raise-syntax-error 'tail "can only be used in a `split'" s)))
(define-syntax-rule (split val yes no)
(if (empty? val)
no
(let ([h (car val)] [t (cdr val)])
(syntax-parameterize ([head (make-rename-transformer #'h)]
[tail (make-rename-transformer #'t)])
yes))))
Now you can fix the problem of evaluating the value multiple times in a
similar way to LoL:
#lang racket
(require racket/stxparam)
(define-syntax-parameter head
(lambda (s) (raise-syntax-error 'head "can only be used in a `split'" s)))
(define-syntax-parameter tail
(lambda (s) (raise-syntax-error 'tail "can only be used in a `split'" s)))
(define-syntax-rule (split val yes no)
(let ([x val])
(if (empty? x)
no
(let ([h (car x)] [t (cdr x)])
(syntax-parameterize ([head (make-rename-transformer #'h)]
[tail (make-rename-transformer #'t)])
yes)))))
And that works fine.
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://barzilay.org/ Maze is Life!