5 ms·
Since this is the largest gathering of LISP users I have seen, I have a question. Why prefer lisp-1 over lisp-2 or vice-versa?
by iFire 2mo ago
Since this is the largest gathering of LISP users I have seen, I have a question.
Why prefer lisp-1 over lisp-2 or vice-versa?
- dismalaf 2mo agoIMO if we look at Lisps today the question looks more like: SBCL, Chez Scheme, Racket or Clojure. Common Lisp and Racket are Lisp-2s but honestly, the namespace thing seems like a minor difference compared to all the other features that differentiate them.
- jwr 2mo agoAs someone who has used both kinds over many, many years: it really doesn't matter.
- guenthert 2mo agoHow about the distinction between the boolean value false, the empty list and whether an empty list ought to be true or false? ]:->
- Jtsummers 2mo agoIgnoring all the other distinctions between lisps, the main difference between lisp-1 and lisp-2 (or lisp-n) is going to be how clean your code looks when you lean into the FP style. In a lisp-2 you'll need to do something like this: (defun apply-twice (f x) (funcall f (funcall f x))) (apply-twice #'1+ 2) Versus this with a lisp-1: (define (apply-twice f x) (f (f x)) (apply-twice 1+ 2) ;; assuming 1+ is defined But there are so many other differences between the lisps in the two categories that this probably won't be the deciding factor for most people.
- wk_end 2mo agoThe argument is: if you don’t have hygienic macros, a Lisp-2 is going to be less brittle than a Lisp-1. The classic example is, imagine you have a function with a local variable called “list”, common enough. Now imagine you invoke a macro inside that function which generates a call to the built-in “list” function - also common enough. In a Lisp-1 without hygiene that breaks - your local definition shadowed the built-in; in a Lisp-2 or hygienic Lisp-1 you’re in the clear.
- nathan_compton 2mo agoPeople should never use non-hygienic macro systems anyway, but even if they are using a non-hygienic macro, they should always use proper hygiene. Its kind of dumb to make the whole system weirder just to avoid issues which should never happen in the first place, in my opinion.
- Jtsummers 2mo ago@nathan_compton, your sibling comment to what I'm writing now is [dead] (not [flagged]) but you're not shadowbanned, newer and older comments are still alive. I vouched for it but it's still [dead], you may want to reach out to the mods.
- deleted 2mo ago[deleted]
- nathan_compton 2mo agoI am a strong proponent of Lisp 1, primarily because the distinction between functions and other types of values is artificial. Functions have first class semantics in Lisp 1 and Lisp 2, but Lisp 2 makes you denote them differently but in an inconsistent manner. Lisp 2 advocates typically make a few arguments. One is that having a separate namespace for functions makes it clearer when you are using a function vs another value. The second is that the evaluator has less work to do when examining the head of a list - it needs only look in the function environment, not the full environment. On the first subject I must disagree - you can bind a function to a regular variable and then use that variable everywhere (except in the car of a list representing a function call), so for most positions in a set of expressions you don't really get information about whether the object being denoted is a function or not. I suppose the second point is somewhat valid, though I suspect if you benchmarked interpreters and compilers it would barely matter. As a person who favors functional programming with a lot of combinators, I find Lisp 2 introduces a lot of pointless noise in the syntax for no reason. And I fundamentally just don't see functions as significantly different sorts of values, so I find the syntactic distinction bizarre.
- sph 2mo agoHaving a separate namespace for functions is silly in that it only saves you from a small set of variable shadowing problems. It’s a hack, not a serious solution.
- zem 2mo agopersonal taste, for the most part. I like the thought of a single namespace, it fits my intuition from pretty much every other language out there, and I like how the code looks when I can pass functions around as though their names are regular variable bindings to an underlying function object.
- coryrc 2mo agoI hate having to come up with different variable names than `list` so I don't shadow the function `list`. Repeat that for a lot of common words: count, first, length, map, max, min, search, string; that you would then actually consider calling said function after declaring a value. Happens to "list" the most, but definitely the others on rarer occasions.
- tmtvl 2mo agoThe usual argument I've seen in favour of Lisp-2 (I personally don't care as much about the function namespace as I do about the type namespace, which I find much more important) is that you can name an argument a conflicting name with a function without the conflict interfering with the code you would write: (defun merge-sort (list before?) (declare (type List list) (type Function before?)) (flet ((merge-2 (a b) (declare (type List a b) (merge 'List a b before?))) (unless (null list) (reduce #'merge-2 list :key #'list)))) (merge-sort '(1 9 8 2 3 4 7 6 5) #'<) Instead of having to name lists 'lst' or something. Which is pretty much personal preference anyway.
- xigoi 2mo agoIsn’t this only a problem if the language is case-insensitive?
- tmtvl 2mo agoOkay, let me give a simpler example: (defun first-two (list) (assert (>= (length list) 2)) (list (first list) (second list))) In a language which doesn't normalise the case of symbols you could in theory work around that by capitalising or upper-casing either the function or the variable, but that's still not a particularly elegant solution.
- kazinator 2mo agoLisp-1 has the wart that it models special operators as bindings in a variable name space. So a form like (print let) actually resolves let to a binding to a special operator, and then has to be somehow pronounced as nonsense. And you can block let from working by binding that as a variable name. Allowing variables to shadow the basic operators of the language is quirky. Would you want to use a weird POSIX shell in which "for x in *.jpg; ..." stopped working because you assigned "for=42"? Yet Lisp-1 has a notational advantage for programs that work with functional values; programs that indirect upon functions are more succinct, free of "shim" operators for lifting values out of the function binding space, or requesting application of a function value. In the TXR Lisp dialect, I worked out a way to have the notational convenience, without bringing in the Lisp-1 issue. 1. The substrate, including macro-expansion logic, is thoroughly Lisp-2. 2. When a compound form is written in square brackets, it indicates that immediate elements (those constituents that are symbols) are to be looked up in a single namespace that is a merger of the function and variable namespaces according to precisely documented rules. This is not recursively applied to the form; its arguments that are compound forms are not treated this way unless they use square brackets. 3. Macros are not affected. In a form [a b c ...], the element a is neither recognized as a special operator or as an operator macro. If it is a symbol, it is either a variable, or a symbol macro. 4. [ ... ] is a surface syntax with a straightforward representation: the (dwim ...) operator. The above semantics is thoroughly baked into the macro expander and correctly treated at all necessary levels of the language: the handling of lexical scopes at expansion time and compilation/evaluation. This system leaves mainly just one small infelicity. The merged 1+2 namespace is not available in certain contexts when it would be handy. Like say we are writing a function that takes a functional argument, that we would like to default: (defun my-sort (sequence : (test-fn equal)) ...) ;; nope! There is no variable equal. We cannot do this: (defun my-sort (sequence : [test-fn equal]) ...) ;; nope! because the (test-fn equal) syntax is not a form to be evaluated; it is just notation within the parameter list which has not been equipped with support for the alternative brackets/dwim structure. The way to do this is: (defun my-sort (sequence : (test-fn (fun equal))) ...) fun* is like Common Lisp's function* operator. It's one of the few instances you ever have to use it, the others also being situations like values in binding constructs such as let. It is not worth complicating things to provide a way to take the fun out these situations.