10 ms·
Lexical vs dynamic scoping in Lisp ...
The first Lisp I ever used was LispKit. Over one weekend I implemented the SECD engine in BCPL, then by hand compiled the LispKit definitions of eval and apply into SECD code and typed them in. Hey Presto! Running Lsip.
But LispKit Lisp used dynamic scoping. If you have an unbound variable in an expression it uses the definition that was in force at the time of the call, not the definition that's in scope from the text of the surrounding code. This was incredibly useful at times, but made debugging and analysis difficult.
To the best of my (very limited) knowledge, modern Lisps use lexical scoping. Are there any current Lisps that use Dynamic scoping?
I could find that out for myself with a lot of searching and reading, but what I really want to know is this:
Do any of you use a system with dynamic scoping?
- Jtsummers 16y agoEmacs Lisp uses dynamic scoping. Common Lisp uses lexical scoping by default, but allows dynamic scoping.
- RiderOfGiraffes 16y agoThanks. Do you know of any detailed discussions of the pros and cons?
- hga 16y agoI don't off hand, but for a start search on the upward funarg problem. Here's the history as I learned it in the '80s (mostly): Original Formula LISP as done by McCarthy was dynamically scoped for any one of a number of reasons: They didn't know better (this is 1958, when LISP was implemented as subroutines callable from FORTRAN running on a vacuum tube IBM 704 and a lot of it was based on a 1955-6 language). They didn't know how to make it efficient, or I think especially interpreted LISP, statically scoped when the latter became recognized as a big and/or the right thing, largely due to Algol 60 (as in 1960). Among other things it's memory hungry (see the Wikipedia Funarg problem article) and there just wasn't much of that to go way back then. The 704 was IBM's first "core by default" machine.... Scheme in 1975 was the first statically scoped LISP although the first production quality version was T in the early '80s (and that was a necessary proof of concept). By the time of Common Lisp in 1984 it had long been recognized that default dynamic scoping was ... not good, Common Lisp's change to default static was perhaps its biggest breaking change and it was years before many code bases were changed to it (by this time various LISP dialects were pretty mature and there were some seriously useful systems written in them like Macsyma and the Lisp Machine). Since then, of all the Lisp dialects I consider to be "major", only Emacs Lisp (ask RMS) and AutoLisp (AutoCad's extension language of 1986 based on "a very early version of XLISP" the date of which I don't know, but XLISP proper started in 1983) are dynamic by default. Everyone else has followed the lead of Scheme and gone default static.
- Jtsummers 16y agoSo hga has the gist of the history, but here's a bit about the use of dynamic vs lexical scoping. Dynamic scoping allows you to do things like: (setq x 10) (defun foo (y) (+ x y)) (foo 3) ;; => 13 (let ((x -10)) (foo 3)) ;; dynamic => -7, lexical => 13 lexical scoping requires you to either explicitly pass this as a parameter: (defun foo (y &optional (x 10)) (+ x y)) (foo 3) ;; => 13 (foo 3 -10) ;; => -7 or modify some value that is in the same scope as foo (lets assume they're both in the global scope for instance): (setq x 10) (defun foo (y) (+ x y)) (let ((old-x x)) (setf x -10) (print (foo 3)) ;; -7 (setf x old-x)) The second one allows us to not change the definition of foo, and the former requires us to be aware that we need to pass this parameter through every function call until foo is reached. On the other hand lexical scoping offers the possibility of closures which dynamic scope does not: (defun bar (x) (lambda (y) (+ x y))) In a lexically scoped language we can then do: (funcall (bar 3) 10) ;; => 13 In a dynamically scoped language if x is not defined the above results in an error, and if it is defined we get something like: (let ((x -10)) (funcall (bar 3) 10)) ;; => -7 Those are, of course, just two (simple) examples. All the above example code should work (unless I copied it wrong, next time I'm writing a comment like this in emacs with slime running) in emacs lisp and Common Lisp if you have either or both available to play with. In emacs lisp they demonstrate dynamic scope, and by using setq in CL it will demonstrate lexical scope. If you want to see dynamic scope in CL try running it with defvar instead. Another example, this is to demonstrate that dynamic scope is also influenced by parameter names: (setq x 10) (defun foo (y) (+ x y)) (defun bar (x) (foo x)) (bar 3) ;; dynamic => 6, lexical => 13 This demonstrates a (potentially) major issue with dynamic scoping (at least as implemented by emacs lisp), that any free variables in functions can be altered by name collisions in any let or parameter list which may occur before it is reached. Edit: Found a couple discussions which add more than what I covered and better demonstrate the utility of dynamic scoping: http://stackoverflow.com/questions/2979428/uses-for-dynamic-scope http://stackoverflow.com/questions/2979428/uses-for-dynamic-... http://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding http://www.emacswiki.org/emacs/DynamicBindingVsLexicalBindin...