Hacker News new | ask | show | jobs
by webreac 1715 days ago
In my lessons about scheme (given by the french translator of TAOCP), I have the following essential characteristics:

- static binding

- closures (true)

- tail recursion

- garbage collector

s-expression or typing is a matter of choice, but, IMHO, if you lack one of the four previous items, it is not really a lisp.

3 comments

Emacs Lisp doesn't have tail call optimization, just like the Lisp it was inspired by.

I'm definitely not an expert in that area, but this list seems kind of arbitrary to me. Especially with s-expressions being optional, which are probably the widest-known feature of the language. According to that definition, Haskell is a "true" Lisp but at least 2 Lisps are not. That makes no sense to me.

The last sentence was from me. The four items are from the SICP (not TAOCP, my mistake). They all are mandated by IEEE scheme standard.

I have encountered many functionnal languages when I was student (caml-light (the ancestor of ocaml), lelisp, gofer (a cousin of haskell), miranda, graal, FP systems, yafool).

The typing may be dynamic or static. The evaluation may be strict or lazy. They may have homoiconicity or a more suggared syntax. All theses choices are valid. These languages have in common the list of fundamental properties. IMHO, this list of 4 items encompass many aspects of SICP. When I evaluate a language, this list helps me understand the qualities and limitations of a language. For example, Perl5 does not have a true garbage collector. javascript does not have tail recursion. Knowing these limitations, I will not code the same way. In Perl5, I will take care of breaking unused circular data. In javascript, I will reorganise highly recursive algorithms.

Almost every language can do tail recursion (in most languages it will blow the stack though) but not every language does tail call optimization. This is a requirement for scheme but it is not for common lisp and many other lisps. So, I don't think this should be on the list.
JavaScript does have tail recursion in the spec, and JSC (Safari) implements it. Other runtimes have thus far refused to because it can impact on debugging I believe.
I feel that static/lexical binding is more of a Scheme thing, though. The Lisp community used to be kind of on the fence on that for a while, even though it ultimately chose to go the same way.

Edit: And to be frank, while dynamic binding may be a horrible mistake in bigger projects, it sometime gives you exactly the easy way out that you may appreciate under time pressure. It's a classical case of "it seemed to be a good idea at the time".

Other than global variables, lisp-2 use lexical scoping …
Whether a Lisp is a Lisp-1 or a Lisp-2 is orthogonal to whether its scoping is lexical or dynamic. There are lexically-scoped Lisp-1s (like Scheme), lexically-scoped Lisp-2s (like Common Lisp), and dynamically-scoped Lisp-2s (like Emacs Lisp). Someone's probably written a dynamically-scoped Lisp-1 at some point, but I can't think of one.
Does Russell's original Lisp count as a Lisp-1? I guess it probably had a common namespace for functions and values.
It's an interesting question; maybe he remembers. If it was a Lisp-1 when originally implemented, it became a Lisp-2 within the first year or two, using different properties of atoms (symbols, as we say now) to evaluate them in "function position" than when they weren't. I suspect he might have made that change before he got it working at all because of properties like FSUBR and FEXPR.

It was definitely dynamically scoped, though.

So it did. But not without controversy. And consequently, there's still defvar. Emacs Lisp is binding dynamically by default to this day, iirc.
It's extremely valuable in Emacs for the same reason it's questionable in other contexts. It lets one person reach out across any scope distance and tweak behavior. Which is exactly what you want when you're configuring your text editor. And exactly what you don't want when you're writing a program for other end users that multiple people are working on.

Emacs Lisp will, I expect, never ever drop 'defvar' dynamic binding. It would break the entire world -- it's relied on far too widely to revoke. Having lexical binding alongside as we do now is probably sufficient.

You mean dynamic scoping and lexical scoping. Dynamic binding is something different; a dynamically-bound subroutine call may call different functions depending on runtime state, for example because it's indirected through a function pointer, for example in the class vtable of a method call receiver.

I agree that it's useful to be able to locally override variables like deactivate-mark and case-fold-search. (This kind of thing makes tail-call elimination more difficult: any dynamically scoped variables must be restored when the "tail-called" function returns.) But there are some other such things in Emacs that can be similarly locally overridden and then restored, but aren't variables: (current-buffer), (point), and (mark), for example, which can be restored with (save-excursion ...). And it's common to have such locally-override-and-restore facilities without using linguistic dynamic scoping for it; PostScript has gsave/grestore, for example, which were copied by Win32 GDI SaveDC and RestoreDC, but that doesn't give C dynamic scoping.

I don't think SaveDC and RestoreDC are known to give rise to problems when "writing a prgoram for other end users that multiple people are working on".

I agree that elisp will never remove dynamically-scoped variables; it would break compatibility with all existing code. Even Common Lisp has "special variables" that behave this way.

I mean, yes, you're not wrong, but the terms within Emacs itself for these features are dynamic/local "binding":

https://www.gnu.org/software/emacs/manual/html_node/elisp/Dy...

https://www.gnu.org/software/emacs/manual/html_node/elisp/Le...

Dynamic scoping relies on a form of dynamic binding: namely the dynamic binding of a symbol to its current value.
MACLISP, Interlisp, Emacs Lisp, AutoLISP, Franz Lisp, and Lisp 1.5 lack all of these except the garbage collector*. Common Lisp and International Standard Lisp additionally have static scoping and consequently closures. Those are essential characteristics of Scheme, not of Lisp.

______

* I'm assuming by "static binding" you mean static scoping; if you actually mean that the association between callsites and functions is statically computable, then it's not even true of Scheme.