4 ms·
I'm new to LISPs. What's the difference between LISP-1 and LISP-2?
by wishinghand 7y ago
I'm new to LISPs. What's the difference between LISP-1 and LISP-2?
- FPGAhacker 7y agoI believe it has to do with namespaces for naming functions vs variables. A lisp-1 has one namespace that encompasses variables and functions. In a lisp-2 they are separate and so it is possible to have a variable with the same name as a function.
- sdegutis 7y agoLisp-1 is pretty much the model JavaScript uses. Lisp-2 is what Emacs uses and it requires some extra features/functions to do functional programming.
- kazinator 7y agoThe nice thing in Lisp-2 is that Lisp code is not only made out of function calls, but also special operators. In a Lisp-1, simple variable bindings can shadow special operators. If you name a variable let, you're locked out of further let binding in that scope. That is ugly. Yet, you can hardly prevent let being used as a variable name. In a Lisp-2, we don't have this problem. We have a more benign version of it, rather: what happens if someone names a function let. That can be dealt with by a compiler warning or error. E.g. our implementation can warn that a special operator is redefined. And then it can cheerfully ignore the redefinition, so that let continues to work. Then there is the question, in a Lisp-1, given that we have a let operator, and operators and variables are in the same space, what the heck is the value of let as a variable? What should (let ((let let)) let) return? This is hand-waved away with something lame like let having an undefined value or whatever. This is ugly and points at Lisp-1 not being as clean and consistent as it is cracked up to be. With macros, it's possible for the same symbol to have both a function and macro binding! In a Lisp-2, that's like 2.5 namespaces. This is the TXR Lisp interactive listener of TXR 219. Quit with :quit or Ctrl-D on empty line. Ctrl-X ? for cheatsheet. 1> (defun foo (arg) (* 10 arg)) foo 2> (defmacro foo (arg) ^'(multiply ,arg by 10)) foo 3> (foo 15) (multiply 15 by 10) 4> (mapcar 'foo '(1 2 3)) (10 20 30) 5> (fboundp 'foo) t 6> (mboundp 'foo) t 7> (mmakunbound 'foo) foo 8> (mboundp 'foo) nil 9> (foo 10) 100 It is said glibly that Lisp-1 dialects uniformly evaluate all positions of a form, rather than treating the leftmost one specially. But that is actually not true. A Lisp-1 dialect, just like Lisp-2, looks at the first position and treats the expression specially if there is a special operator or macro there! (But: not if it's a symbol macro; yet, symbol macros and macros end up in the same namespace.) Moreover, after a function call form like (f x y) is evaluated (uniformly, to be sure), the semantics isn't uniform: the first value denotes a function (or other callable object), and the remaining values denote arguments to be applied to it. That's a fundamental asymmetry, shared with Lisp-2. Why fret about symmetry in the evaluation, when the semantics of the application itself is ultimately asymmetric. In the end it just boils down to the pragmatics: hygiene considerations versus not having to use funcall for working with higher order functions.
- NhanH 7y agoLisp-1 has one namespace for both function and variables. Lisp-2 has separate namespaces
- rags2riches 7y agoIn a lisp-1, you name your list variables lst. In a lisp-2, they are called list.
- owl57 7y agoNot sure if that's a good example. My lists of foos are usually named "foos", not "list" or "lst".
- bjoli 7y agoI come from a scheme tradition where higher order functions are used for many things. I often use the name lst for generic lists. I am not so sure I think it matters. In the place where it matters the most (macros) scheme has macro hygiene. That discussion is not worth having here though, because it is really just a matter of taste. I like both ways, but prefer the scheme way where you have to explicitly break hygiene instead of explicitly try to not shadow bindings . I understand that defmacro can be nice sometimes, and for that most schemes provide it (heck, even racket has it!).
- owl57 7y agoNow that I think of it, I certainly have tripped on "list" arguments in Python, but adopted haskellers' "xs" for such generic uses and forgot about it. BTW, GHC throws especially funny messages when you define a variable like "head" or "last" and then try to call the shadowed function. OTOH, Emacs' python-mode highlights builtins regardless of context, very convenient to catch such lapses immediately.
- ljm 7y agoEssentially: (set foo ‘bar) (defun foo () ‘baz) foo ;; ‘bar (foo) ;; ‘baz You can have both a variable and a function named the same way in the same scope.
- dreamcompiler 7y agoCommon Lisp is a Lisp-2; Scheme is a Lisp-1. Lisp-2 is handier [to me] in many ways but it requires an inelegant syntax when you want to evaluate a function that returns a function and then call said returned function. Assume you have a function foo that when called returns the function '+. To then call that function with args 1 & 2 you'd say in a Lisp-1: ((foo) 1 2) ; 3 while in a Lisp-2 you'd have to say: (funcall (foo) 1 2) ; 3
- sdegutis 7y agoIn what ways is it handier? I've never heard of benefits of Lisp-2. Whereas Lisp-1 is more intuitive to those who know JavaScript/Lua/etc.
- kazinator 7y agoLisp-2 is intuitive to those who know shell. foo() { echo hi } $ foo=1 $ foo hi $ echo $foo 1 $ var=$(foo) $ echo $var hi $ var=$foo echo $var 1 Separate space for variables and functions/commands. Hardly anyone bats an eyelash about this. This provides a hygiene benefit similar to what we have in a Lisp-2. Specifically, suppose we do this: $ ls=abc by doing that, we have not shadowed the ls command. We can name shell variables without worrying about that sort of clash. This is so obvious, you don't see it being spelled out to anyone.
- dreamcompiler 7y agoExactly this. When I write Scheme code, it trips me up when I have to re-spell variable names to avoid clashes. Experienced Scheme programmers don't find this difficult; they think it's weird to have to use funcall. It's just a function of what you're more used to.
- kazinator 7y agoIt happens in C, when you have short function names. In the C internals of TXR, I have to be careful about using variable names like list or cons, because then those functions are not available. A certain list accumulating macro calls the tail function. If I accidentally introduce a variable called tail where this macro is used, oops! In typical C code, you're protected from clashes by using short variable names, and long function names.