807 karma · joined October 19, 2016
(set-macro-character
#\$ #'(lambda (stream char)
(declare (ignore char))
`(uiop:getenv ,(symbol-name (read stream)))))
This lets you read environment variables like $home, and write to them like ordinary variables, eg (setf $message "hello"). It reads the variable name as a Lisp symbol, so by default it will be upcased; this happens to be convenient for environment variables, but as with any symbols in Lisp, you can escape lowercase characters using either \ before the character or |'s around the whole symbol, or you could change the readtable case, or just preserve case initially in the read macro.It is permissible to place an s-expression pattern in place of a symbol in the (sym init) form of an element of the bvlspec. The symbols in the pattern will become bound to the matching elements of the initialization (see DESETQ). This kind of pattern-matched assignment is called destructuring.
(let (((a b) '(1 2)))
(cons b a))
=> (2 . 1)
0: http://www.maclisp.info/pitmanual/contro.html#5.6.1Then they'd be happy with Lisp from 40 years ago. Common Lisp (which was not the first to have either feature) has DESTRUCTURING-BIND (as well as some other constructs like extended lambda lists and within LOOP) where you can use destructuring, and it has generic sequence functions that work on more than lists like MAP, REMOVE-IF-NOT, REDUCE, etc, as well as lookup functions for getting things out of key-value maps (list-based or not) by key, or from sequences by index. Explicitly cdring down lists is mostly only done in introductory textbooks.
It feels like a lot of Clojure advocacy is written in terms of this "Lisp before Clojure" strawman of some minimalist Scheme subset. Lisp has had arrays, dictionaries, records, etc (with literal syntax and proper accessors), as well as destructuring and generic sequence functions, for almost half a century (more than half a century for some of that). Whether or not you prefer Clojure's syntax for those things, Lisp had those things long before Clojure existed.
I agree, but Scheme passes this test more than you might think. When it originally appeared, it really didn't stand out from the crowd of Lisp 1.5 successors that much at all. It even piggybacked off of its host Maclisp implementation, directly calling Maclisp functions extensively from Scheme, to provide any functionality that there wasn't any point re-implementing (ie, most of it). It was basically just a Maclisp package.
Modern Scheme still has a lot of features in common with Maclisp of this time (and by extension Common Lisp), like DO (although Scheme's do has slightly different semantics because it's a macro over tail recursion rather than gotos; subtle differences like this are to be expected between dialects), and it's still based off list structure in exactly the same way that Lisps always have been, so there's still CONS, CAR, CDR, RPLACA (renamed set-car!), RPLACD (renamed set-cdr!), NCONC (renamed append!), and so on.
Scheme's lexical scoping also made it back into mainline Lisp, and was a pretty big contribution to Common Lisp. A lot of features like CLOS, SETF, and DEFMACRO (none of which were in Lisp 1.5) are very commonly provided by Scheme implementations.
Using the historical argument, there's no question at all that Scheme is Lisp. The majority of ancient Lisp programs can be run under modern Schemes with simple compatibility packages. When all the other Lisps either merged into CL or died, though, Scheme continued to be actively used, and as time went on the Scheme community shared less and less code with the mainline Lisp community. Some people argue that with the modern state of things it's not really useful to talk about Scheme as Lisp now, because they kind of "left" Lisp and went off on their own, philosophically. Scheme definitely began as just another Lisp, though.
You might not see it much in beginner materials, but idiomatic Lisp code tends to be fairly object-oriented, using the Common Lisp Object System (CLOS). CLOS is still a bit different from OOP in a language like Java, though: it's a much more powerful object system.
"=" means equality in math; a = a + 1 is the same as 1 = 2 because if you subtract a from both sides and add 1 to both sides you get 1 = 2.
Lambda calculus has the concept of binding variables, but it doesn't use "=" for that, it uses application of lambda forms. It's the same idea that's applied in some variants of Lisp, where LET is a macro such that (let ((x 1) (y 2)) ...) expands to ((lambda (x y) ...) 1 2).
The way it plays out is that rebinding is perfectly fine, because it's not really any different from binding in the first place. The same way that
(let ((x 10))
(f x)
(setf x (1+ x))
(g x))
can be re-written as ((lambda (x)
(f x)
(setf x (1+ x))
(g x))
10)
That can itself be re-written as: ((lambda (x)
(f x)
((lambda (x)
(g x))
(1+ x)))
10)
If you'd like to read more about this sort of thing, Sussman and Steele's "Lambda: The Ultimate Imperative" is a good starter: http://repository.readscheme.org/ftp/papers/ai-lab-pubs/AIM-...LET was virtually always optional; an expression by itself on a line (like 'X <> 10') is a syntax error anyway, so removing LET doesn't introduce any ambiguity.
If there's a huge difference between the impact that being silenced has on someone compared to having their primary source of income abruptly taken away, I would expect the latter to be worse for most people.
Do you mean that (:td :colspan "2" "username:") is verbose and error-prone compared to <td colspan="2">username:</td>, or what are you comparing it to? That code is probably denser than I would have written it, but I don't see any issue related to the fact the HTML is being generated from Lisp.
This example doesn't take advantage of it, but writing HTML in Lisp is easier and less error-prone than regular HTML, because you can abstract patterns and factor things.
Lisps often provide built in editors; Common Lisp even has the standard function ED for invoking one[0], but like most of the "environment" functions its behaviour is left almost entirely unspecified. Lisp is also usually developed in an image-based fashion. Images are one of the features that separate Lisp and Smalltalk from almost everything else, not one that separates them from each other. Some Lisps provide access to source code from function objects, with varying degrees of integration, and again, Common Lisp provides the standard function FUNCTION-LAMBDA-EXPRESSION for this purpose[1]. They don't generally work via decompilation, but neither does Smalltalk.
0: http://www.lispworks.com/documentation/HyperSpec/Body/f_ed.h...
1: http://www.lispworks.com/documentation/HyperSpec/Body/f_fn_l...
They're more flexible than hash tables and often faster; what makes them so awful? They both have trade-offs that make them better and worse in different contexts, depending what you're optimising for.
> Rather unlikely. The namespaces are pretty easy to understand conceptually, and their interface is pretty minimal.
Packages can definitely give beginners trouble. I don't think it's that uncommon for newcomers to Lisp to have REPL sessions like this:
CL-USER> (ql:quickload :iterate)
(:ITERATE)
CL-USER> (iter (for i below 10) (collect i))
;; Error: 3 undefined functions and two undefined variables
Then they find out they need to use the package first: CL-USER> (use-package :iterate)
;; Error: 3 NAME-CONFLICTs saying that the symbols ITER and friends
;; are already accessible
Then they throw their hands in the air about how frustrating Lisp tooling is. For a less obvious example, imagine they had just been using LOOP normally at some point in the past in that REPL session before trying to bring ITERATE in. Once you understand how symbols and packages work, it's easy to see why it works the way it does, but it's also easy to see how a beginner could get totally lost.CAR and CDR are operations on conses, so you can't have them without conses; that's like having sqrt without numbers. Lisp has FIRST and REST for operating on lists, and so does Clojure. Lisp also has CAR and CDR for operating on conses, but as Clojure doesn't have conses, it doesn't have CAR and CDR, either.
> Processors are no more based on accumulator and decrement registers
CAR and CDR were named for 'contents of address part of register' and 'contents of decrement part of register', not anything about accumulator or decrement registers. I'm not sure where you heard that processors don't have accumulators anymore, but that's definitely not true. The reason Clojure doesn't have CAR and CDR doesn't have anything to do with obsolete processors, though.
> Clojure is based on immutability like Scheme
Scheme is not based on immutability.
> CL is often used in a mutable way (see the shameful f-set).
Do you mean SETF? FSet[0] is an immutable data structure library for Common Lisp. A lot of Schemes have SETF, too, by the way, there's even an SRFI[1]. But SETF is just so that you can have one generic assignment operator; standard Scheme has plenty of commonly-used, destructive assignment operators like set-car!, set-cdr!, vector-set!, etc.
0: https://common-lisp.net/project/fset/Site/FSet-Tutorial.html
You can't have atom without conses (well, it would just be (lambda (x) t)); which Clojure function are you thinking of?
No, ASDF and Quicklisp do different things. ASDF handles compiling and loading systems, whereas Quicklisp handles downloading systems and their dependencies. ASDF is kind of like make, and Quicklisp is kind of like a package manager.
I understand how currying works, thanks, but that doesn't change the fact that $ is not equivalent to apply. A version of apply that dealt with curried functions would just have to fold over a list, and I don't think it could be properly generalised in Haskell's type system (but I"m not an expert at Haskell).
For your definition of apply, Lisp-1's don't have it, because as you said, you'd just call the damn function. Lisp-2's sort of have it, but it's called funcall. Regardless, your definition of apply is not what apply is in Lisp.
I think you could make the argument that Emacs Lisp is as close. Emacs Lisp has some major similarities with Maclisp that CL doesn't share (like in variable scoping and obarrays and such). Elisp and CL are both very close relatives of Maclisp; Elisp was designed as a mini-Maclisp for a text editor, whereas Common Lisp was designed as Maclisp's successor.
Some people might consider it pedantic to insist to someone who was confused by a choice of wording, and offered a less potentially confusing alternative, that actually it's fine because it's technically correct, regardless of how people understand it when they read it.