From: Mirko on
I have a utility that may need to do some unit conversions. I would
like to specify the conversion rules as an association list:

((a do-something) (b do-something-else) ... etc)

Now, I could specify `do-something' as
#'(lambda (arg)
(* arg 5))

and so on. All the lambda's have the same format #'(lambda (arg)
(...)), only the ellipsis changing from item to item

Specifying the lambdas works, but makes the a-list hard to read.
Instead I would like it to contain the bodies of the lambda's.

((a (* arg 5)) (b (/ arg 12)) ... etc)

So, the question is how to compile those bodies (I am not sure
`compile' is the right term - I just want to get an `executable'
lambda out of these).

Right now, I am compiling via macros:

(defmacro build (sym)
`(lambda (arg)
,(second (assoc sym +table+)))

and then

(funcall (build a) arg)

will execute my function.

But I am wondering if I am misusing the macros here, and if there is
something more direct

Thanks,

Mirko
From: Tamas K Papp on
On Mon, 05 Apr 2010 08:27:52 -0700, Mirko wrote:

> I have a utility that may need to do some unit conversions. I would
> like to specify the conversion rules as an association list:
>
> ((a do-something) (b do-something-else) ... etc)
>
> Now, I could specify `do-something' as #'(lambda (arg)
> (* arg 5))
>
> and so on. All the lambda's have the same format #'(lambda (arg)
> (...)), only the ellipsis changing from item to item
>
> Specifying the lambdas works, but makes the a-list hard to read. Instead
> I would like it to contain the bodies of the lambda's.
>
> ((a (* arg 5)) (b (/ arg 12)) ... etc)
>
> So, the question is how to compile those bodies (I am not sure `compile'
> is the right term - I just want to get an `executable' lambda out of
> these).
>
> Right now, I am compiling via macros:
>
> (defmacro build (sym)
> `(lambda (arg)
> ,(second (assoc sym +table+)))
>
> and then
>
> (funcall (build a) arg)
>
> will execute my function.
>
> But I am wondering if I am misusing the macros here, and if there is
> something more direct

I think that this is OK, but I would do it in a slightly different
way. You want convenient syntax for two things: defining new
conversions, and performing them by name. I would just use a macro
for the first and a generic function for the second, eg:

(defgeneric do-conversion (name arg)
(:documentation "Perform the named conversion on ARG."))

(defmacro define-conversion (name &body body)
`(defmethod do-conversion ((name (eql ',name)) arg)
,@body))

(define-conversion m->km (/ arg 1000))
(define-conversion km->m (* arg 1000))

;; example:
(do-conversion 'm->km 1000)

Best,

Tamas
From: joswig on
On 5 Apr., 18:24, Tamas K Papp <tkp...(a)gmail.com> wrote:
> On Mon, 05 Apr 2010 08:27:52 -0700, Mirko wrote:
> > I have a utility that may need to do some unit conversions.  I would
> > like to specify the conversion rules as an association list:
>
> > ((a do-something) (b do-something-else) ... etc)
>
> > Now, I could specify `do-something' as #'(lambda (arg)
> >       (* arg 5))
>
> > and so on.  All the lambda's have the same format #'(lambda (arg)
> > (...)), only the ellipsis changing from item to item
>
> > Specifying the lambdas works, but makes the a-list hard to read. Instead
> > I would like it to contain the bodies of the lambda's.
>
> > ((a (* arg 5)) (b (/ arg 12)) ... etc)
>
> > So, the question is how to compile those bodies (I am not sure `compile'
> > is the right term - I just want to get an `executable' lambda out of
> > these).
>
> > Right now, I am compiling via macros:
>
> > (defmacro build (sym)
> >   `(lambda (arg)
> >      ,(second (assoc sym +table+)))
>
> > and then
>
> > (funcall (build a) arg)
>
> > will execute my function.
>
> > But I am wondering if I am misusing the macros here, and if there is
> > something more direct
>
> I think that this is OK, but I would do it in a slightly different
> way.  You want convenient syntax for two things: defining new
> conversions, and performing them by name.  I would just use a macro
> for the first and a generic function for the second, eg:
>
> (defgeneric do-conversion (name arg)
>   (:documentation "Perform the named conversion on ARG."))
>
> (defmacro define-conversion (name &body body)
>   `(defmethod do-conversion ((name (eql ',name)) arg)
>      ,@body))
>
> (define-conversion m->km (/ arg 1000))
> (define-conversion km->m (* arg 1000))
>
> ;; example:
> (do-conversion 'm->km 1000)
>
> Best,
>
> Tamas

How about this one without generic functions.
Dispatch is done by symbol lookup if necessary.

(defparameter *table*
'((m->km (/ arg 1000))
(km->m (* arg 1000))))

(defmacro defconv (var conversions)
(when (symbolp conversions)
(setf conversions (symbol-value conversions)))
`(progn ,@(loop for (name calculation) in conversions
collect `(defun ,name (,var)
,calculation))))

(defconv arg *table*)

(m->km 4)

(funcall 'm->km 4)

(defun convert (how what)
(funcall how what))

(convert 'm->km 4)





From: Pascal J. Bourguignon on
Mirko <mirko.vukovic(a)gmail.com> writes:

> I have a utility that may need to do some unit conversions. I would
> like to specify the conversion rules as an association list:
>
> ((a do-something) (b do-something-else) ... etc)
>
> Now, I could specify `do-something' as
> #'(lambda (arg)
> (* arg 5))
>
> and so on. All the lambda's have the same format #'(lambda (arg)
> (...)), only the ellipsis changing from item to item
>
> Specifying the lambdas works, but makes the a-list hard to read.
> Instead I would like it to contain the bodies of the lambda's.
>
> ((a (* arg 5)) (b (/ arg 12)) ... etc)
>
> So, the question is how to compile those bodies (I am not sure
> `compile' is the right term - I just want to get an `executable'
> lambda out of these).
>
> Right now, I am compiling via macros:
>
> (defmacro build (sym)
> `(lambda (arg)
> ,(second (assoc sym +table+)))
>
> and then
>
> (funcall (build a) arg)
>
> will execute my function.
>
> But I am wondering if I am misusing the macros here, and if there is
> something more direct

This looks ok. Indeed, if your problem is to make a form more readable,
using syntactic abstraction may be useful. Now the question is whether
you need to do that at run-time or at compilation-time.

If you need to do it at compilation-time, ie. if all your expressions
are known at compilation-time, like are literally written in your
program source, then you may indeed use a macro to wrap them up in a
function.

On the other hand, if you build this a-list at run-time, then you don't
need a macro, you can do it with a function.





The fact hat you use +table+ ie. a constant would let me think that this
is indeed a compilation time matter. Then you don't have to wait
run-time to build the function, you can do it at compilation time:

(defun generate-function (arg-name body)
`(lambda (,arg-name) ,@body))

(defun generate-function-alist (alist)
(cons 'list
(mapcar (lambda (entry)
(list 'cons
(list 'quote (car entry))
(generate-function 'arg (cdr entry))))
alist)))

(defmacro function-alist (alist)
(generate-function-alist alist))

(defconstant +table+ (function-alist ((a (+ arg 1)) (b (* arg 4)))))

+table+
--> ((A . #<FUNCTION :LAMBDA (ARG) (+ ARG 1)>)
(B . #<FUNCTION :LAMBDA (ARG) (* ARG 4)>))

so now you can use directly:

(funcall (or (cdr (assoc key +table+))
(lambda (arg)
(declare (ignore arg))
(error "No function keyed ~S" key)))
arg)





Of course, if you know your alist only at run-time you would just use
instead:

(defun make-function-alist (alist)
(mapcar (lambda (entry)
(cons (car entry)
(compile nil `(lambda (arg) ,@(cdr entry)))))
alist))


eg.:

(defparameter *table* nil)
(setf *table* (make-function-alist (acons 'a (read)
(acons 'b (read) *table*))))
(+ arg 3) (/ arg 3) ; read by the above form
--> ((A . #<COMPILED-FUNCTION NIL>) (B . #<COMPILED-FUNCTION NIL>))


If you don't mind compiling the functions, you may use:
(coerce `(lambda (arg) ,@(cdr entry)) 'function)
instead, to get whatever the implementation deems convenient.


--
__Pascal Bourguignon__
From: Thomas A. Russ on
Mirko <mirko.vukovic(a)gmail.com> writes:

> I have a utility that may need to do some unit conversions. I would
> like to specify the conversion rules as an association list:
>
> ((a do-something) (b do-something-else) ... etc)
>
> Now, I could specify `do-something' as
> #'(lambda (arg)
> (* arg 5))
>
> and so on. All the lambda's have the same format #'(lambda (arg)
> (...)), only the ellipsis changing from item to item
>
> Specifying the lambdas works, but makes the a-list hard to read.
> Instead I would like it to contain the bodies of the lambda's.
>
> ((a (* arg 5)) (b (/ arg 12)) ... etc)
>
> So, the question is how to compile those bodies (I am not sure
> `compile' is the right term - I just want to get an `executable'
> lambda out of these).
>
> Right now, I am compiling via macros:
>
> (defmacro build (sym)
> `(lambda (arg)
> ,(second (assoc sym +table+)))

I don't think you need a lambda expression here. I would use a function
instead:

(defun build (sym)
`(lambda (arg)
,(second (assoc sym +table+))))

That way you don't have to specify the symbol argument as a literal
constant in your code. You can evaluate it, pass it in using a
parameter to a function that needs it, etc.

> and then
>
> (funcall (build a) arg)

(funcall (build 'a) arg)

> will execute my function.

If you want to compile this, you can get a compiled varsion like this:

(compile nil (build 'a))

> But I am wondering if I am misusing the macros here, and if there is
> something more direct

Well, what I would do is use the simple format as the input and storage
format in your code. Then for actual use, I would first do an
initialization step where you just go through and replace the
expressions with their compiled functions.

I would also use a true association list like

((a . (* arg 5)) (b . (/ arg 12)) ... etc)

So, for example:

(defun initialize ()
(loop for item in *table*
do (setf (cdr item)
(compile nil (build (cdr item))))))

And then to use it:

(defun convert (key arg)
(funcall (cdr (assoc key *table*)) arg))

So this doesn't require any macros at all. If you wanted to use a macro
in all of this, I would use it to build the conversion table instead.
That way you can do away with the need to explicitly call an
initialization function in the first place:

(defvar *table* nil)

(defmacro conversion-table (&rest key-body-pairs)
`(setf *table*
,(loop for (key . body) in key-body-pairs
collect (cons key
(compile nil `(lambda (arg) ,body))))))

And then you would have

(conversion-table
(a . (* arg 5))
(b . (/ arg 12))
...)

and you would just use the CONVERT function from above.

Note: If you don't like the dot-notation ALISTS, you can always not use
the dot and substitute SECOND for CDR and LIST for CONS in the above
treatement. The only thing using the CONS/dot notation does is save one
cons cell per entry.

--
Thomas A. Russ, USC/Information Sciences Institute