How are function parameters stored in lisp?
Asked Answered
M

3

5

I assumed that values passed into a lisp function are assigned to a quote matching the name of the parameter. However, I was surprised that this:

(defun test (x) (print (eval 'x)))
(test 5)

doesn't work (the variable x is unbound). So if parameters aren't stored as symbols in the function, what exactly IS x in this example? Is there a way to access parameters from a symbol matching the parameter name?

More context: What I would like to do is something like this:

defun slice (r1 c1 r2 c2 board)
  (dolist (param '(r1 c1 r2 c2))  ;adjust for negative indices
    (if (< (eval param) 0)
      (set param (+ (length board) (eval param)))))
        ;Body of function

Basically, I want to iterate through the first four parameters and make an adjustment to any of their values if they are < 0. Of course, I could do a let and have an individual line for each parameter, but considering I'm doing the same thing for each of the four parameters this seemed cleaner. However, I get the error that the variable R1 is unbound.

Maugre answered 16/10, 2013 at 15:15 Comment(1)
It is implementation specific. SBCL give some information in the Foreign Function Interface chapter, etc.Sextain
P
4

Is there a way to access parameters from a symbol matching the parameter name?

Not for lexical binding. Common Lisp gives no way to access a lexical variable from a similar named symbol. You would need to declare the variable special.

So if parameters aren't stored as symbols in the function, what exactly IS x in this example?

A processor register? A part of a stack frame?

With dynamic binding:

CL-USER 40 > (defun foo (a b)
               (declare (special a b))
               (dolist (v '(a b))
                 (if (zerop (symbol-value v))
                     (set v 10)))
               (values a b))
FOO

CL-USER 41 > (foo 1 0)
1
10
Phelgen answered 16/10, 2013 at 15:27 Comment(4)
Thanks, this allowed me to use the same code with just one extra line to declare the parameters as specialMaugre
@murphyslaw Do be sure that you understand the other possible consequences of declaring a variable special. The variable will be bound with dynamic scope which means, e.g., that (let ((x 3)) (declare (special x)) (funcall (let ((x 4)) (declare (special x)) (lambda () x)))) will return 3, not 4, even though when the lambda function is created x is bound to 4.Hickox
@JoshuaTaylor - Thanks for the heads up, I clearly need do do some more reading on this. Just to be sure, declaring a parameter as special will not have any implications for that symbol outside of the function block, correct?Maugre
I suppose it depends on what exactly you mean by outside, but take a look at these examples.Hickox
C
5

That's basically how lexical binding works: the variable name gets replaced within the lexical scope with a direct reference to where the variable's value is stored. Binding the variable name's symbol-value is only done for dynamic variable which you can declare with special.

One way to avoid repeating yourself would be a macro:

(defmacro with-adjusting ((&rest vars) adjust-value &body body)
  `(let ,(loop for var in vars
               collect `(,var (if (minusp ,var)
                                (+ ,var ,adjust-value)
                                ,var)))
     ,@body))

(defun slice (r1 c1 r2 c2 board)
  (with-adjusting (r1 c1 r2 c2) (length board)
    ;; function body
Chancelor answered 16/10, 2013 at 16:42 Comment(1)
That's a good answer. A macro is quite useful here. Small drawback: the compiled code gets larger.Phelgen
P
4

Is there a way to access parameters from a symbol matching the parameter name?

Not for lexical binding. Common Lisp gives no way to access a lexical variable from a similar named symbol. You would need to declare the variable special.

So if parameters aren't stored as symbols in the function, what exactly IS x in this example?

A processor register? A part of a stack frame?

With dynamic binding:

CL-USER 40 > (defun foo (a b)
               (declare (special a b))
               (dolist (v '(a b))
                 (if (zerop (symbol-value v))
                     (set v 10)))
               (values a b))
FOO

CL-USER 41 > (foo 1 0)
1
10
Phelgen answered 16/10, 2013 at 15:27 Comment(4)
Thanks, this allowed me to use the same code with just one extra line to declare the parameters as specialMaugre
@murphyslaw Do be sure that you understand the other possible consequences of declaring a variable special. The variable will be bound with dynamic scope which means, e.g., that (let ((x 3)) (declare (special x)) (funcall (let ((x 4)) (declare (special x)) (lambda () x)))) will return 3, not 4, even though when the lambda function is created x is bound to 4.Hickox
@JoshuaTaylor - Thanks for the heads up, I clearly need do do some more reading on this. Just to be sure, declaring a parameter as special will not have any implications for that symbol outside of the function block, correct?Maugre
I suppose it depends on what exactly you mean by outside, but take a look at these examples.Hickox
S
2

As Rainer explained, you cannot access the lexical argument value by its name.

What you can do instead is use the &rest argument together with destructuring-bind if you want the variables too:

(defun slice (board &rest params)
  (destructuring-bind (r1 c1 r2 c2)
      (mapcar (lambda (param) ;adjust for negative indices
                (if (minusp param)
                    (+ (length board) param)
                    param))
              params)
    ... operate on r1 c1 r2 c2 ...))
Synthiasyntonic answered 16/10, 2013 at 16:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.