4 ms·
Why would functions in a lisp-2 be more awkward than in a lisp-1? PHP has been too long ago for me to understand your comparison. Would you please explain it
by aerique 10y ago
Why would functions in a lisp-2 be more awkward than in a lisp-1?
PHP has been too long ago for me to understand your comparison.
Would you please explain it differently?
- Jtsummers 10y agoIn a lisp-1: (define inc (lambda (n) (+ n 1))) When referring to `inc` (assuming no shadowing in a different scope), `inc` refers to a function object, we can do: (define g (lambda (f) (f 3)) (g inc) ;; yields 4 In a lisp2, it won't work the same way: * (defun inc (n) (+ 1 n)) * (defun g (f) (f 3)) ; in: DEFUN G ; (SB-INT:NAMED-LAMBDA G ; (F) ; (BLOCK G (F 3))) ; ; caught STYLE-WARNING: ; The variable F is defined but never used. ; in: DEFUN G ; (F 3) ; ; caught STYLE-WARNING: ; undefined function: F ; ; compilation unit finished ; Undefined function: ; F ; caught 2 STYLE-WARNING conditions Instead we can define it like: * (defun g (f) (apply f '(3))) ;; specifying the parameters as a list * (g 'inc) ;; get the symbol for inc, as a variable it has no binding 4 * (g inc) debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "main thread" RUNNING {10028F6AF3}>: The variable INC is unbound. There are some benefits, though it's been so long since I've used either Scheme or Common Lisp that I can't clearly articulate why, but I recall at the time preferring lisp2 (or lispN, really).
- zeveb 10y agoI'm pretty sure that the lispy way to write the example would be: (defun g (f) (funcall f 3)) (g #'inc) Which I think is actually prettier than the Scheme: (define g (lambda (f) (f 3)) (g inc)
- Jtsummers 10y agoThanks. Couldn't remember funcall but I knew there was a better way.
- lisper 10y agoIt is not hard to make a Lisp2 act like a Lisp1 when you want it to, e.g.: (defmacro define (name&args &body body) (let ((name (car name&args)) (args (cdr name&args))) `(defun ,name ,args (flet ,(mapcar (lambda (arg) `(,arg (&rest args) (apply ,arg args))) args) ,@body)))) ? (DEFINE (F X) (X 1)) F ? (f '1+) 2 It is much (much!) harder to make a Lisp1 act like a Lisp2.
- chameco 10y agoIn any languages with multiple namespaces, anything inhabiting some non-primary namespace (i.e., not the matrix containing normal variables) feel second class. Emphasis "feel", and hence "awkward" over "technically limiting". Additionally, it makes the transition to lambda calculus more difficult to grok, which somewhat hinders understanding. It's pretty trivial to make some simple rewrite rules from (most of) Scheme to lambda calculus: it's a two-hour project at most. Doing this for CL is much less intuitive/elegant, possibly making it more difficult for those with that sort of theoretical background.
- netsettler 10y agoIt is expressly not the goal of CL to be all things to all people, and yet to be many things to many people. Scheme has a kind of fixed set of things it definitely wants to be and sacrifices others, it just does it in a different shape so that the particular examples you pick are easy. One way I sometimes conceive it is that in any given language there are a certain number of small expressions and a certain number of large ones. Differences in semantics don't make things non-computable (which is why turing equivalance is boring) but they change which expressions will be easily reachable. There are certain things Scheme wants to be able to say in not many characters and different things CL does. Neither is a flawed design. But they satisfy different needs. It's possible to dive into either and be fine. As others have pointed out here, it's not as big a deal in practice as it seems like in theory. What matters in practice is to have an intelligible and usable design, which both languages do. But to assume that the optimal way to say something in one language should stay constant even if you change the syntax and semantics of the language is to not understand why you would want to change the syntax and semantics of the language.
- kazinator 10y agoIn Lisp-1 dialects such as Scheme, macros are not in the "matrix containing normal variables". You cannot say with a straight face that "all elements of a form are evaluated equally and then the rest values are applied as args to the first value" because the counterexample (let ((a 42) a) doesn't work that way. The Lisp-1 has to treat the leftmost position specially to determine whether let is a macro to be expanded or a special operator. That will not happen in a form like this (list 3 let 4). In the TXR Lisp dialect (which provides Lisp-1 and Lisp-2 style evaluation), I fix this. In the Lisp-1 style forms, macros are not allowed. So [let ((a 3)) a] is a priori nonsense. The let symbol's position is evaluated exactly like the other positions, without being considered a macro (other than a symbol macro, which all the other positions may be). The combination of Lisp-2 and Lisp-1 in one dialect let me have a cleaner, purer Lisp-1 in which that half-truth about all positions of a Lisp-1 form being equally evaluated is literally true, always. Lisp-2 for macros and special ops, Lisp-1 for HOF pushing: beautiful. (list list) works, no funcall anywhere, and even if let is a variable that holds a function [let 42] call the damn thing: 1> (let ((let (lambda (n) n))) [let 3]) 1 The let operator is not shadowed: 2> (let ((let (lambda (n) n))) (let ((a 'b)) [let a])) ;; let var visible across inner let b Basically, as far as I'm concerned, this whole Lisp-1 versus Lisp-2 squabbling is an obsolete debate and solved problem (by me).
- chriswarbo 10y ago> Why would functions in a lisp-2 be more awkward than in a lisp-1? Jtsummers explained this quite well (as does the article). > PHP has been too long ago for me to understand your comparison. > Would you please explain it differently? PHP has a function namespace, which is used by C-style function definitions (similar to 'defun'): function foo(x) { return x; } and a separate value namespace, which is used by anonymous functions (similar to 'lambda'): $bar = function(x) { return x; }; Both sorts of function can usually[1] be called directly, e.g. foo(42); // Returns 42 $bar(42); // Returns 42 Passing around functions as values is tricky though. It's easy for the lambda-style values: array_map($bar, [0, 1, 2, 3]) For named functions, we have to provide a string containing the function's name, e.g.: array_map('foo', [0, 1, 2, 3]) This is very similar to the quoting of names in a lisp-2 (although using strings seems much uglier to me). [1] See https://news.ycombinator.com/item?id=8119419 https://news.ycombinator.com/item?id=8119419 for some (historical?) counterexamples
- taeric 10y agoIt is probably more accurate to say referencing functions is more awkward. Just doing a standard function call looks the same in both. Specifically (function arguments). However, in a Lisp1, referencing the function is to just name it. In a Lisp2, referencing the function requires you to use a special syntax. So, as an example, passing it as an argument to another function, in Lisp1: (other-function function), in Lisp2 (other-function #'function). Note the #' in before function. There are a few other restrictions, they are all in the linked page. That make sense?