Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does SBCL for lisp handle scope differently? It does not seem to pass scope into called functions?

When using emacs or my android app I run

(defun big (num) (setf num2 5)(little num)))
(defun little (num)(+ num2 num))

Little happily accepts num2 but when I run it in my SBCL repl (with sublimetext3) it does not.

Is this correct?

What is a workaround without creating a global variable for num2?

I could just pass a second argument (little num num2)

But this wont work when I am trying to mapcar little over a list. Because I can only have one argument when mapcaring correct?

like image 704
Gregg Arthur Evans Avatar asked Dec 24 '22 10:12

Gregg Arthur Evans


1 Answers

Please read §6. Variables from Practical Common Lisp.

Unlike Emacs Lisp, Common Lisp relies on lexical scope by default (Emacs Lisp is dynamic by default). Dynamic scope (i.e. indefinite scope and dynamic extent) is provided by declaring variables special, and by convention, they are written with asterisks around their names (named "earmuffs"), like *standard-output*. You use defparameter or defvar to declare those variables. Since it has a global effect, you should never use them from inside functions; likewise, your usage of setf is not defined in Common Lisp: no variable named num2 was declared previously in the scope; besides, even if it did, using a global/special variable for local variable is bad style.

Dynamic scope

With special variables, you can for example locally rebind the standard output: the new value is only visible while the code is inside the body of the let binding:

(let ((*standard-output* *error-output*))
  (print "Stream redirection"))

By default, print writes to the stream bound to *standard-output*; here, the stream is locally bound to the one given by *error-output*. As soon as you escape the let, *standard-output* reverts to its previous value (imagine there is a stack).

Lexical scope

With lexical scope, your code can only access the bindings that are visible in the text surrounding your code (and the global scope), and the extent is indefinite: it is possible to access a binding (sometimes indirectly) even after the code returns from the let:

(let ((closure
        (let ((count 0))
          (lambda () (print (incf count))))))
  (funcall closure)
  (funcall closure))

;; prints:
;; 1
;; 2

The lambda expression creates a closure, which captures the variable named count. Every time you call it, it will increase the count variable and print it. If you evaluate the same code another time, you define another closure and create another variable, with the same name.

Mapcar

Because I can only have one argument when mapcaring correct?

Not exactly; the function called by mapcar should be able to accept at least as many elements as the number of lists that are given to it (and it should also not require more mandatory arguments):

(mapcar (lambda (x y) (* x y))
        '(1 2 3)
        '(0 3 6))
=> (0 6 18)

(mapcar #'list '(1 2) '(a b) '(+ /))
=> ((1 a +) (2 b /))

The function can also be a closure, and can use special variables.

... with a closure

(defun adder (x)
  (lambda (y) (+ x y)))

(mapcar (adder 10) '(0 1 2))

=> (10 11 12)

The adder functions takes a number x and returns a closure which accepts a number y and returns (+ x y).

... with a special variable

If you prefer dynamic scope, use earmuffs and give it a meaningful name:

(defparameter *default-offset* 0)

... and define:

(defun offset (x)
  (+ x *default-offset*))

You can then mapcar too:

(let ((*default-offset* 20))
  (mapcar #'offset '(1 2 3)))

=> (21 22 23)

As said by jkiiski in comments, you can also declare special variables with (declare (special ...)) where you usually put declarations (when entering a let, a defun, ...). You could also use the special operator progv. This can be useful to have "invisible" variables that are only known by a set of functions to exchange information. You rarely need them.

like image 95
coredump Avatar answered May 15 '23 04:05

coredump