1

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?

7
  • Why would you want to do that? What did you try? Commented Mar 9, 2015 at 10:45
  • I want to use the scheme code in common lisp ,kind of uniform the code by the scheme code. Commented Mar 9, 2015 at 11:42
  • The biggest problem is local define that looks the same as global define but need to be rewritten as a labels. define is perhaps the messiest special form in Scheme. Commented Mar 9, 2015 at 12:38
  • So you actually want to write a Scheme interpreter in Common Lisp. That seems a little broad. Commented Mar 9, 2015 at 12:48
  • Oh,yeah. I just wonder whether somebody do simulate the exactly behavior of define in common lisp with macro or something , so i would not need change the code by hand. Commented Mar 9, 2015 at 12:54

2 Answers 2

3

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)
Sign up to request clarification or add additional context in comments.

1 Comment

Yeah, this is what i think of.
2

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.

  1. 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.

  2. 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

It's really messy to have to join the two namespaces. Even worse to catch define in lambdas (all varities) and turn them into labels. Its probably easier to port the code than make macros that does that.
@Sylwester Absolutely agreed. The argument lists are probably one of the easiest things to handle.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.