[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [plt-scheme] syntax rules

Quoth Mike T. Machenry:
> i am attempting to learn how to define syntax's in scheme. i decided
> to try to make a toy type system as my first try. the problem i'm
> having is that each identifier in my lambda needs to be put in two
> different places (the assertion and the parameter) and i can't seem to
> do that with a recursive syntax. also my attempt seems to be currying
> the functions so it takes one paramter at a time. i can see how it
> does that but i'm not sure how to do it correctly. also there is a
> shortage of syntax-rules docs on the web, in help-desk, the r5r and in
> the ansi scheme book. does anyone know of a place to read up on this?

It's all there in chapter 11 of the mzscheme language manual, though
that is admittedly not the easiest document to read and use as a
tutorial. :)

> expand this
> (type-lambda ((nunber? foo) (number? bar) (string? baz) un-typed-param)
>   (if (= foo bar)
>       baz
>       un-typed-param))
> [...]
> this is my attempt
> (define-syntax type-lambda
>   (syntax-rules ()
>     ((_ ((type var) var2 ...) exp1 ...)
>      (lambda (var)
>        (type-assert type var)
>        (type-lambda (var2 ... ) exp1 ...)))
>     ((_ (var var2 ...) exp1 ...)
>      (lambda (var)
>        (type-lambda (var2 ...) exp1 ...)))
>     ((_ () exp1 ...)
>      (lambda ()
>        exp1 ...))
The first problem you'll have is in getting type-assert to be known to
type-lambda at macro-expansion time.  There are a couple of ways to do
this; if type-assert is in a separate module, you can require-for-syntax
that module.  If type-assert is only used by type-lambda, then you might
want to define it inside type-lambda---like using a "let" to define a
helper function.  However, in macros, identifiers bound with "let" are
not accessible in the expansion; you'll need "with-syntax" for that
purpose.  That brings us to

(define-syntax type-lambda
  (with-syntax ([type-assert (lambda (type? itm)
                               (unless (type? itm) (error "foo")))])
    (syntax-rules ()
      [(_ ((type var) var2 ...) exp1 ...)
       (lambda (var)
	 (type-assert type var)
	 (type-lambda (var2 ... ) exp1 ...))]
      [(_ (var var2 ...) exp1 ...)
       (lambda (var)
	 (type-lambda (var2 ...) exp1 ...))]
      [(_ () exp1 ...)
       (lambda ()
	 exp1 ...)])))

Next, you're going to run into arity problems.  In particular, if the
type-lambda form has multiple arguments, its expansion will be a curried
function that returns a thunk.  If I define the function fromk as follows:

(define fromk (type-lambda ([list? l] [string? s] x) (printf "~a~a~a" l s x))) 

its expansion (omitting type assertions) will be

#'(lambda (l)
    (lambda (s)
      (lambda (x)
        (lambda () (#%app (#%top . printf) (#%datum . "~a~a~a") l s x)))))

which means that to call it with the arguments lst, str, and num, I have
to write

> ((((fromk lst) str) num))

in order to get it to actually print, which is probably not what you
want.  This issue is a little trickier, as you'll need to have the
outer-level lambda in the expansion accept all the arguments at the same
time, but you still want to write your macro recursively.  This is a
pretty cool problem that (unfortunately) I don't have time to work on
further, but I suspect that the solution will involve actual computation
during macro expansion, which can be tricky.  You'll probably want to
read up on how syntax-case works, for starters.  Work on this for a
while, and let us (me) know when you get stuck again. :)

-=-Don Blaheta-=-=-dpb@cs.brown.edu-=-=-<http://www.cs.brown.edu/~dpb/>-=-
When your hammer is C++, everything begins to look like a thumb.
					--Steve Hoflich, comp.lang.c++