5 ms·
Lisp-1 Has Won
- kazinator 6y ago> Almost every other somewhat popular language chooses to share a single namespace between functions and variables. Almost every popular language is also not a Lisp at all. That must also be a well-informed, modern design decision; therefore, just don't make your language a Lisp. > Examples include Clojure, Janet, Python, Java, JavaScript, and even Rust. The latter three are completely unsurprising given that Algol, Pascal and C have one namespace for functions and variables. Oh, and object file formats for assembly languages have one namespace for functions and variables: there are just symbols which name anything. However: object file formats are actually smarter than that: they have named sections, which give the user any number of namespaces. You can have dynamic symbols, debug symbols, symbols needed during initialization only, ... Within a given section, though, symbols are in one namespace. You can't have an x function and x storage location both in the .text section. Speaking of low-level, not all machines put code and data into the same space. > The set of LISP2 languages is quite small these days. Besides Common Lisp and Emacs Lisp, both of which are over three decades old at this point, there are also Ruby and Perl. Algol traces back as far as 1958. The hypothesis that the single namespace is a more modern development doesn't hold water. When taking a historic look, you have to consider some of the years that were before you were born.
- kazinator 6y agoTXR Lisp dates back to 2009 (actually later, because TXR didn' expose a Lisp initially). It's a Lisp-2. I'm extremely well-informed on the Lisp-1 versus Lisp-2 topic; about as well as anyone can be, which makes me incapable of making a wrong choice. Yet, I made it fundamentally a Lisp-2. However, I also imbued it with a way of expressing in a Lisp-1 style, because there is value in being able to work with higher order functions without clutter. Lisp-1-like evaluation is implemented as a special operator called dwim. (dwim f x y ...) evaluates the argument expressions in the same way. If any argument is a symbol, it is evaluated in a combined namespace consisting of the visible function and variable bindings. Other arguments are evaluated in the ordinary way. The programmer normally doesn't write (dwim f x y ...) but rather uses the read syntax [f x y]. There is certain psychology at play in the [f x y] syntax. Objects other than functions are callable. For instance, a vector and array are callable. [vec 0] denotes the first element. Lots of languages use [ ] for indexing, and this plays into that. There is also a rule that f may not be a macro or special operator. So [let (a b) ...] is nonsense, unless there is a binding for let so it resolves to a function. In any case, it doesn't refer to the let special operator. The [f ...] notation assures the reader that f is a callable object, and not an operator. The symbolic arguments of dwim may be symbol-macros: (symacrolet ((a f) (b x)) [a b]) is equivalent to [f x]. The existence of separate symbol macros and operator macros is one of the arguments for Lisp-2. They are different. An operator macro M can only be used in the (M ...) position. Macros aren't stored in variables or passed around. A symbol macro and operator macro are processed similarly in that they are both replaced during expansion, yet differently; they do not have uniform evaluation. The user of TXR Lisp knows that there can be no special operator f or operator macro f which intercepts the meaning of [f x y ...]. It is absolutely functional; the arguments are subject to the same evaluation treatment regardless of what is in the environment. In a block of code in which all parentheses are square, you know that there are no special operators or macros whatsoever; every name you see is resolved in the function/variable namespace (or else is a symbol macro) and all evaluation proceeds by application. Looking at this backwards: the [f x y ...] application-only, no-operators syntax could be obtained very easily in a Lisp-1 dialect, and without complications, as a sugar for some (call f x y ...) form. Done! But since f has moved to the second position, Lisp-1 versus Lisp-2 is no longer entirely relevant to it. We can have that notation in a Lisp-2 if all we do is turn call into a somewhat smarter operator which knows how to resolve symbolic arguments across both namespaces.
- lispm 6y agoI didn't know there was a contest. Not mentioned: R and Elixir are 'lisp-2'.
- kazinator 6y agoPOSIX shell is a Lisp-2. foo is a function (if in the first position of a command), otherwise just a "word": foo foo bar # look for foo function or command, pass "foo" and "bar" arguments The prefix $ must be used in any position in order to resolve a variable name. $command $foo bar # substitute $command and $foo, then re-parse A function foo and variable foo can co-exist: foo=/usr/bin/foo foo() { print $foo # no conflict } GNU Make is a Lisp-1, but needs a $(call F,X Y) operator to pass arguments to a macro F defined as F := <body> where in <body> the insertion of arguments is indicated using $(1), $(2), ... All the disadvantages of Lisp-1 with all the disadvantages of Lisp-2.