I wonder how to simulate the scheme define in common lisp,and want to write a macro for simulate the define. So what's the different between cl's defun deparameter defvar and scheme's define ,and how i can do this?
2 Answers
define in Scheme is not easily implemented in Common Lisp. It's not easily implemented in Scheme either. It's transformation is different in different scopes:
(define test value) ; defines test, value can be anything even (lambda ..) which makes a procedur object
(define (test arg ...) body ...) ; defines the procedure test
(define (somefun)
(define test ...)
(define (test2 x) ...)
...)
Is a fancey way of writing:
(define (somefun)
(letrec ((test ...)
(test2 (lambda (x) ...))
...))
So what are the eqvivalents in Common Lisp:
(define myplus otherfun) ; (setf (symbol-function 'myplus) otherfun)
(define myplus (lambda args . body)) ; (defun myplus args . body)
(define (myplus . args) . body ) ; (defun myplus args . body)
(define value 10) ; (defparameter value 10)
Here is my take on the macro. Still this won't work for internal defines:
(defmacro define (name-or-ll &body expressions)
(flet ((dotted-to-rest (lst)
(let ((last (last lst)))
(if (null (cdr last))
lst
(append (butlast lst)
(list (car last) '&rest (cdr last)))))))
(cond ((consp name-or-ll) ; (define (fun a b) ...)
`(progn
(defun ,(car name-or-ll) ,(dotted-to-rest (cdr name-or-ll)) ,@expressions)
(defparameter ,(car name-or-ll) #',(car name-or-ll))))
((and (consp (car expressions)) ; (define fun (lambda (a b) ...))
(eq (caar expressions) 'lambda))
`(define (,name-or-ll ,@(cadar expressions)) ,@(cddar expressions)))
(t `(let ((value ,(cons 'progn expressions)))
(when (functionp value)
(setf (symbol-function ',name-or-ll) value))
(defparameter ,name-or-ll value))))))
(define proc (lambda (x) (* x x)))
(define myproc proc)
(define myplus #'+)
(define test 'test)
(define (testproc a b) (+ a b))
(define testproc2 (lambda (a . b) (apply myplus a b)))
(list (proc 10) (myproc 10)
(myplus 2 3) (testproc 2 3)
(testproc2 2 2 1) (funcall testproc2 2 2 1)
test) ; => (100 100 5 5 5 5 TEST)
1 Comment
While it's not clear why you'd want to do this, a rough approximation can be had with:
(defmacro define ((name &rest args) &body body)
`(defun ,name ,args
,@body))
Then, for instance:
(pprint (macroexpand-1 '(define (xcons x y) (cons x y))))
; (DEFUN XCONS (X Y)
; (CONS X Y))
There are two issues with this.
This allows more than just Scheme arglists. (I.e., it "leaks" a bit.) If you put in variables like &rest, &optional, &key, or &aux that Common Lisp will treat specially, you'll get strange behavior. E.g., if you do:
(pprint (macroexpand-1 '(define (foo a &rest b) (list a b)))) ; (DEFUN FOO (A &REST B) (LIST A &REST B))In Scheme, this would be a function of three arguments, but in Common Lisp, it's a function of any positive non-zero number of arguments.
This doesn't allow all Scheme arglists. In particular, it doesn't handle dotted arglists, as in:
(define (list* x . xs) (cons x xs))
To handle these types of cases, you'll need a somewhat more sophisticated argument list translation. Here's one that returns a new arglist that's acceptable for Common Lisp functions, and which restablishes the bindings of the variables in the original Scheme arglist within the body of the function:
(defun cl-arglist (scheme-arglist)
"Return a Common Lisp arglist corresponding to the Scheme
arglist, and a list of bindings (as for LET) that will
re-establish the variables declared in the original Scheme
arglist."
(labels ((convert (s-arglist cl-arglist bindings)
(cond
((atom s-arglist)
;; dotted arglists
(unless (null s-arglist)
(let ((cl-var (gensym)))
(setq cl-arglist (list* cl-var '&rest cl-arglist)
bindings (list* (list s-arglist cl-var) bindings))))
(values (nreverse cl-arglist)
(nreverse bindings)))
;; variable in arglist
(t
(let ((s-var (first s-arglist))
(cl-var (gensym)))
(convert (rest s-arglist)
(list* cl-var cl-arglist)
(list* (list s-var cl-var) bindings)))))))
(convert scheme-arglist '() '())))
(defmacro define ((name . arglist) . body)
(multiple-value-bind (arglist bindings) (cl-arglist arglist)
`(defun ,name ,arglist
(let ,bindings
,@body))))
For instance:
CL-USER> (pprint (macroexpand-1 '(define (list . args)
args)))
; (DEFUN LIST (&REST #:G1068)
; (LET ((ARGS #:G1068))
; ARGS))
That said, you'll need more than this to make Scheme code run without modification in Common Lisp. Scheme only has one namespace, whereas Common Lisp has multiple namespaces. That means that in Scheme you can write:
(let ((f (lambda (x y) (+ x y))))
(f 2 3))
;=> 5
In Common Lisp you have to write:
(let ((f (lambda (x y) (+ x y))))
(funcall f 2 3))
;=> 5
I'm not sure whether there's any code analysis you could perform that could determine all the cases in which you'd need to turn (f 2 3) into (funcall f 2 3) and in which you would not.
2 Comments
define in lambdas (all varities) and turn them into labels. Its probably easier to port the code than make macros that does that.
labels.defineis perhaps the messiest special form in Scheme.