(function arglist e0 ...)
that is transformed into
(lambda some-stuff e0 ...)
where some-stuff is the output of a user-supplied function that takes
arglist as a parameter. I don't know how to properly compose a define-
syntax form so that some-stuff is evaluated in order to yield the
lambda list for the final lambda form.
In case I'm not being clear, here's an example:
Suppose a user writes
(function ((x <integer>)(y <integer>))
(+ x y))
I want to transform this into
(lambda (x y)
(+ x y))
...but what I don't want to do is write a macro that recognizes the
form ((x <integer>)(y <integer>)) and extracts the x and y. Instead, I
want to call a user-supplied function that extracts the x and y, so
that users can define their own patterns for such parameter lists.
I don't know the proper way to make this happen using define-syntax.
Does someone else?
It is of paramount importance that you tell which Scheme
implementation you plan to use. For the R6RS Schemes, what
you want is possible by putting the custom transform
procedure in a library which is loaded for expansion time:
;;; the library --
(library (arg-transformer)
(export transform)
(import (rnrs))
(define (transform arglist)
(let ((args (map car arglist)))
(write (list 'input arglist))(newline)
(write (list 'output args))(newline)
args)))
;;; end of file
;;; the program
(import (rnrs)
(for (arg-transformer) expand))
(define-syntax my-lambda
(lambda (stx)
(syntax-case stx ()
((?keyword (?arg ...) ?form0 ?form ...)
(let ((args (syntax->datum (syntax (?arg ...)))))
(with-syntax ((ARGS (datum->syntax (syntax ?keyword)
(transform args))))
(syntax (lambda ARGS ?form0 ?form ...))))))))
(define blue
(my-lambda ((x <double>) (y <double>))
(write (list x y))
(newline)))
(blue 1 2)
;;; end of file
Much depends upon how you want the user to supply its
custom transformer function.
HTH
--
Marco Maggi
Using syntax-rules:
(define-syntax function-transformer
(syntax-rules ()
((_ helper)
(syntax-rules ()
((_ args . body)
(helper args body))))))
(define-syntax function-a-helper
(syntax-rules ()
((_ ((var type) ...) body)
(lambda (var ...) . body))))
(define-syntax function-a
(function-transformer function-a-helper))
(define-syntax function-b-helper
(syntax-rules (:)
((_ args body)
(function-b-helper args body ()))
((_ (var : type . rest) body (vars ...))
(function-b-helper rest body (vars ... var)))
((_ () body vars)
(lambda vars . body))))
(define-syntax function-b
(function-transformer function-b-helper))
(expand '(function-a ((x <foo>) (y <bar>)) (list x y)))
=> (lambda (x y) (list x y))
(expand '(function-b (x : <foo> y : <bar>) (list x y)))
=> (lambda (x y) (list x y))
Using procedural macros and syntax-case:
(define (function-transformer args-transformer)
(lambda (stx)
(syntax-case stx ()
((_ args . body)
#`(lambda #,(args-transformer #'args) . body)))))
(define-syntax function-a
(function-transformer
(lambda (args-stx)
(syntax-case args-stx ()
(((var type) ...)
#'(var ...))))))
(define-syntax function-b
(function-transformer
(lambda (args-stx)
(let recur ((args-stx args-stx))
(syntax-case args-stx (:)
((var : type . rest)
(cons #'var (recur #'rest)))
(()
'()))))))
(expand '(function-a ((x <foo>) (y <bar>)) (list x y)))
=> (lambda (x y) (list x y))
(expand '(function-b (x : <foo> y : <bar>) (list x y)))
=> (lambda (x y) (list x y))
--
: Derick
----------------------------------------------------------------
> (function arglist e0 ...)
>that is transformed into
> (lambda some-stuff e0 ...)
>Suppose a user writes
I am going to assume R6RS here. You can do this, but it's dangerous.
It's very easy for an user to provide a procedure that completely
ignores the hygiene, and if you expect a procedure, it makes it that
much more difficult to deal with the wrapped syntax objects that you
invariably must pass. In other words, you are forcing the user of this
form to write procedural macros, when in actuality, syntax-rules can
easily do many of these sorts of transformations directly. The same
macro or procedure needed to write the argument list processor is almost
exactly the same as the macro/procedure needed to write the new special
lambda form. That is, there isn't much difference between:
(define-syntax new-lambda
(syntax-rules (<integer>)
[(_ ((x <integer>) ...) e0 e1 ...)
(lambda (x ...)
(assert (for-all integer? (list x ...)))
e0 e1 ...)]))
and
(define-syntax <integer> ---)
(define-syntax arg-processor
(lambda args
(for-all (lambda (x) (free-identifier=? (cadr x) <integer>)) args)
(map car args)))
(define-lambda new-lambda arg-processor)
In fact, the first one is far better and easier to write. The second one
doesn't do nearly as much, and is harder to understand. If you need to
somehow parameterize this, you can do that, and you can allow the user
to specify their own lambda form that they want to use. This will get
you the flexibility to let the user do what they want, and still make it
much easier to write correct code. I think this is a case of abstracting
where the abstraction doesn't make much sense.
Aaron W. Hsu
Your example is broken, because it does not preserve the lexical
context info of identifiers.
> ;;; the library --
>
> (library (arg-transformer)
> (export transform)
> (import (rnrs))
> (define (transform arglist)
> (let ((args (map car arglist)))
> (write (list 'input arglist))(newline)
> (write (list 'output args))(newline)
> args)))
>
> ;;; end of file
>
> ;;; the program
>
> (import (rnrs)
> (for (arg-transformer) expand))
>
> (define-syntax my-lambda
> (lambda (stx)
> (syntax-case stx ()
> ((?keyword (?arg ...) ?form0 ?form ...)
> (let ((args (syntax->datum (syntax (?arg ...)))))
> (with-syntax ((ARGS (datum->syntax (syntax ?keyword)
The above two lines are the problem.
> (transform args))))
> (syntax (lambda ARGS ?form0 ?form ...))))))))
For example:
(library (your-lambda)
(export your-lambda)
(import (rnrs)
(arg-transformer))
(define-syntax your-lambda
(lambda (stx)
(syntax-case stx ()
((?keyword (?arg ...) ?form0 ?form ...)
(let ((args (syntax->datum (syntax (?arg ...)))))
(with-syntax ((ARGS (datum->syntax (syntax ?keyword)
(transform args))))
(syntax (lambda ARGS ?form0 ?form ...)))))))))
(library (build-on-your-lambda)
(export build-on-your-lambda)
(import (rnrs)
(your-lambda))
(define-syntax build-on-your-lambda
(syntax-rules ()
((_ (var ...) . body)
(your-lambda ((var <foo>) ...) . body)))))
((build-on-your-lambda (x y) (list x y))
1 2)
ERROR=>
Unhandled exception
Condition components:
1. &undefined
2. &who: eval
3. &message: "unbound variable"
4. &irritants: (x)
--
: Derick
----------------------------------------------------------------
(Oops, that should have been a reply to Mikel's initial message, not
to Marco's reply. I hadn't yet noticed Marco's message and thought it
was Mikel's message.)
--
: Derick
----------------------------------------------------------------
This might be a good start:
(define-syntax function
(syntax-rules ()
((_ ((?arg _) ...) ?body ...)
(lambda (?arg ...) ?body ...))))
It works in R5RS and R6RS.
It works, but doesn't accomplish what I described.Rather it
accomplishes what I explicitly said that I DON'T want to do: it
recognizes a particular syntax and transforms it into a lambda.
I don't want to recognize a particular syntax in the arglist; I want
to pass the arglist to a user-supplied function and use the output of
that function as the arglist of the lambda form. It looks like a
couple of the other suggestions may work; thanks, guys.
Sorry Mike. I heretofore promise never to reply to posts during a
conference call again.
Here's a little more context.
The job of the function form is to provide a convenient syntax for
creating polymorphic functions. In order to be a convenience, such a
form needs to support a parameter-list format that accommodates the
qualifications that users want to place on argument values in order to
select the appropriate method. Okay; what qualifications do they want
to place on argument values? Well, that depends on the dispatch
mechanism.
In the system in question, dispatch mechanisms are user-defined. We
cannot know in general what the dispatch mechanism will be; that's up
to the user. That means we can't know in the general case what kinds
of qualifications the users will want to state about arguments. They
might want to add qualifiers per-argument; or they might want to place
constraints on entire lambda lists as a whole (both are possible in
the system under discussion). So what's a convenient syntax for
stating such qualifications?
Thus far, rather than trying to decide what the right syntax is for
all seasons, I've provided users the tools needed to write their own
lambda-list parsers. When a user designs a dispatch mechanism, he or
she can at the same time define the kind of lambda-list that mechanism
will use in function definitions.I have several iterations of
implementations that provide those amenities, but now I'm responding
to a user request for the Scheme implementation of the system to use
r5rs and define-syntax, for the sake of portability.
With luck, I'll find a happy solution amongst these various replies to
my question. Or, who knows, maybe someone will propose a grand
galactic lambda-list syntax that will be adequate to all conceivable
dispatch mechanisms.
The easiest is probably to provide a purely procedural interface, and
let extensions provide their own define-xyz-method macros. It's easy
enough to write such macros, and there is not a lot to gain from having
a single defmethod form accept all kinds of syntaxes. Depending on the
kind of extension, you may actually also want to extend the body of a
macro (for example, in order to provide variations of call-next-method).
It's hard to predict what else a user extension may want to do.
The R6RS module allows renaming identifiers on import, so whatever
define-xyz-method macros are defined, they can be used as 'plain'
defmethod macros in user code, if needed. So there is no serious
restriction in that regard here either.
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
I did it that way in the first several iterations, but it seemed very
clunky. Maybe if I give it another go, I can get a better result. I'll
give it a shot.
Thanks for the suggestion, Pascal. It's a good one. I'm targeting
r5rs. This implementation exists because of a user request for a
portable version, which makes r5rs more suitable in my mind, because
it's going to be more widespread for a while yet.
There are a few annoyances that come with basing the implementation on
r5rs. One is that there isn't a handy way to make reflection on
generic functions efficient, because you can't conveniently look
inside a closure, and you can't add a new kind of callable object, so
doing things like obtaining the method-table of a generic function
involves something like looking the closure up in an alist, rather
than storing the method table on the closure. (There are other ways,
sure; I could cobble up a sentinel value, for example, and have the gf
dispatch code switch to 'reflection mode' when the sentinel appears in
the inout, but that seems like unnecessarily hairing up an
implementation that is meant mainly for illustrative purposes.)
Another annoyance is that there isn't a way in r5rs to do (bound?
thingie) or (defined? thingie), which means you can't make a defmethod
for your domain. defmethod in CLOS creates a toplevel binding for a
generic function if one doesn't exist, which is a nice simplifying
convenience for writing polymorphic functions. It means that in normal
code, generic function definitions are always introduced by defmethod
forms, and there is no distinguished defining form that has to appear
first in the evaluation order. I don't see a clean way to do that in
r5rs. Users will therefore be obliged to use a defining form to
introduce a variable bound to a generic function, and then separately
use add-method! to add methods to it.
>Here's a little more context.
>The job of the function form is to provide a convenient syntax for
>creating polymorphic functions. In order to be a convenience, such a
>form needs to support a parameter-list format that accommodates the
>qualifications that users want to place on argument values in order to
>select the appropriate method. Okay; what qualifications do they want
>to place on argument values? Well, that depends on the dispatch
>mechanism.
>In the system in question, dispatch mechanisms are user-defined. We
>cannot know in general what the dispatch mechanism will be; that's up
>to the user. That means we can't know in the general case what kinds
>of qualifications the users will want to state about arguments. They
>might want to add qualifiers per-argument; or they might want to place
>constraints on entire lambda lists as a whole (both are possible in
>the system under discussion). So what's a convenient syntax for
>stating such qualifications?
I'm a bit confused. If the user has to write their own dispatcher, and
their own syntax recognizer for method definitions, what is there left
for you to do? Can you give an usage example which demonstrates these
features and why you need this function? If I understood a little better
the work you want your users to do, and the work you don't want them to
do, I could probably figure out an efficient syntax for it that provides
the flexibility you desire.
My main point was that writing the argument list procedures will be as
hard to write as the syntax that recognize the argument lists if it is
done right.
Aaron W. Hsu
>Thanks for the suggestion, Pascal. It's a good one. I'm targeting
>r5rs. This implementation exists because of a user request for a
>portable version, which makes r5rs more suitable in my mind, because
>it's going to be more widespread for a while yet.
When an user requests a portable version, that usually means they have
some implementation in mind that it won't run on right now. With a
judicious choice of libraries to use and the appropriate abstraction,
you can make portable code without having to rely purely on something
like R5RS. Many systems have R6RS level features even if they don't
support R6RS specifically, meaning that you can often rely on those
features even though they may not have all the same interfaces.
Aaron W. Hsu