Live data from Hacker News

Practical Common Lisp (2009)

gigamonkeys.com

61–70 of 91 posts

Re: Practical Common Lisp (2009)

#61
post #10

Can anyone please comment what advantages and short-comings Common LISP has over "modern LISP" (i.e. Clojure)?

Common Lisp advantages:

- "more interactive" resumable exceptions, better code reloading functionality

- multiple values (cumbersome to use for superficial reasons, but with much better semantics than in e.g. scheme, for sure and probably also Lua)

- compiler macros

- readtable macros (although the design leaves to be desired)

- more sophisticated pretty printer

- more "target platforms"; with clojure it's basically jvm or, to a lesser extent, JS. If you dislike both you're out of luck

- there's a treasure trove of old but interesting material that uses Common Lisp (like PAIP by Norvig)

- much nicer for writing highly imperative code

Common Lisp disadvantages:

- Common Lisp has no eco system to speak off (but at least there's less churn than in clojure land)

- the language is frozen/dead

- career suicide, even compared to clojure

- no high quality free implementations (for example sbcl has a rather poor garbage collector compared to java, go and other widely used languages)

- single precision is de-facto the default floating point type

- built around cons cells which are a terrible data structure: slow, cumbersome and brittle; there's OK support for vectors and slightly worse support for multidimensional arrays and hash-tables and very poor support for generic collection manipulation or efficient functional data structures

- no out of the box support for laziness

- no concurrency or parallelism abstractions

- no good standard pattern matching library

- brain drain: very few smart people doing interesting things left (most left by the 90ies)

- often feels plodding and cumbersome compared to clojure (destructuring, basic string or sequence operations)

Re: Practical Common Lisp (2009)

#62
post #51

Earlier quoted context omitted.

Did you change from map to reduce because map needs to be told what kind of sequence to create? In Clojure the literal syntax avoids that. In addition there is this caveat about the literal vector syntax in Common Lisp: You can use the #(...) syntax to include literal vectors in your code, but as the effects of modifying literal objects aren't defined, you should always use VECTOR or the more general function MAKE-AR…

The literal syntax has nothing to do with MAP. I can define a MAP which returns a vector or a list, depending on the input: CL-USER 111 > (defun maps (function sequence) (map (type-of sequence) function sequence)) MAPS CL-USER 112 > (maps #'1+ '(1 2 3)) (2 3 4) CL-USER 113 > (maps #'1+ #(1 2 3)) #(2 3 4) I can also write a compiler macro, so that the implementation is chosen at compile time, when a literal data objec…

So you admit the syntax is not as brief or as clean when you lose Clojure's data structure literals, and you have to write additional code for each type to get there?

You haven't even touched on sets, which in CL are not a built in type, even though there are functions that let you pretend a list is a set, it's not really the same thing when it's not an efficient underlying representation.

Personally I wouldn't call Common Lisp a low level language. A programmable language sure. Low level implies being close to the machine architecture, which it clearly is not.

Re: Practical Common Lisp (2009)

#63
post #10

Can anyone please comment what advantages and short-comings Common LISP has over "modern LISP" (i.e. Clojure)?

The community in clojure has been friendly in my experience. Unfortunately they’ve moved to slack for their communication. The clojure group on google is pretty stagnant which is too bad. Topic threads are the exception in a chat room. Conversations in a forum like groups can serve as their own documentation. Chat logs have to be deciphered.

There is Clojureverse

Re: Practical Common Lisp (2009)

#64
post #51

Earlier quoted context omitted.

The literal syntax has nothing to do with MAP. I can define a MAP which returns a vector or a list, depending on the input: CL-USER 111 > (defun maps (function sequence) (map (type-of sequence) function sequence)) MAPS CL-USER 112 > (maps #'1+ '(1 2 3)) (2 3 4) CL-USER 113 > (maps #'1+ #(1 2 3)) #(2 3 4) I can also write a compiler macro, so that the implementation is chosen at compile time, when a literal data objec…

So you admit the syntax is not as brief or as clean when you lose Clojure's data structure literals, and you have to write additional code for each type to get there? You haven't even touched on sets, which in CL are not a built in type, even though there are functions that let you pretend a list is a set, it's not really the same thing when it's not an efficient underlying representation. Personally I wouldn't call…

I don't know car/cdr/cadr/caar etc might not be close to the current machine architecture... But to me at least it feels quite low level. Perhaps similar to some of the unfortunate parts of the jvm/byte code spec that held back/convoluted dynamic languages on the jvm for a long while.

Re: Practical Common Lisp (2009)

#65
post #51

Earlier quoted context omitted.

The literal syntax has nothing to do with MAP. I can define a MAP which returns a vector or a list, depending on the input: CL-USER 111 > (defun maps (function sequence) (map (type-of sequence) function sequence)) MAPS CL-USER 112 > (maps #'1+ '(1 2 3)) (2 3 4) CL-USER 113 > (maps #'1+ #(1 2 3)) #(2 3 4) I can also write a compiler macro, so that the implementation is chosen at compile time, when a literal data objec…

So you admit the syntax is not as brief or as clean when you lose Clojure's data structure literals, and you have to write additional code for each type to get there? You haven't even touched on sets, which in CL are not a built in type, even though there are functions that let you pretend a list is a set, it's not really the same thing when it's not an efficient underlying representation. Personally I wouldn't call…

> So you admit the syntax is not as brief or as clean when you lose Clojure's data structure literals, and you have to write additional code for each type to get there?

I didn't admit anything like that. I showed you that vectors are well integrated in Common Lisp.

> Personally I wouldn't call Common Lisp a low level language.

I wouldn't either.

But then I didn't call Common Lisp a low level language. I said: 'Here (and in many other places) Common Lisp is a low-level language' - which means that Common Lisp is PARTLY a low-level language - for example no detection of modification of literal data is required/provided - the programmer has to make sure data can be modified when necessary. A higher-level data-structure would probably require detecting this or would provide only immutable data types. Common Lisp has many relatively low-level features, from cons cells to directives telling the compiler to avoid runtime type checks. Having singly-linked lists is much lower-level than persistent sequences of Clojure.

Generally it's not surprising that Common Lisp has lots of low-level features, since it has been used to write much of itself and large parts of its runtime - where Clojure is a hosted language: large parts of the runtime, the compiler and parts of the library are not written in Clojure: https://github.com/clojure/clojure/tree/master/src/jvm/cloju...

Re: Practical Common Lisp (2009)

#67
post #65

Earlier quoted context omitted.

So you admit the syntax is not as brief or as clean when you lose Clojure's data structure literals, and you have to write additional code for each type to get there? You haven't even touched on sets, which in CL are not a built in type, even though there are functions that let you pretend a list is a set, it's not really the same thing when it's not an efficient underlying representation. Personally I wouldn't call…

> So you admit the syntax is not as brief or as clean when you lose Clojure's data structure literals, and you have to write additional code for each type to get there? I didn't admit anything like that. I showed you that vectors are well integrated in Common Lisp. > Personally I wouldn't call Common Lisp a low level language. I wouldn't either. But then I didn't call Common Lisp a low level language. I said: 'Here (…

I see what you mean. CL is low level in that it exposes implementation details of some of its data structures and let's you use them at that level. Sure that is powerful.

Re: Practical Common Lisp (2009)

#68
post #64

Earlier quoted context omitted.

So you admit the syntax is not as brief or as clean when you lose Clojure's data structure literals, and you have to write additional code for each type to get there? You haven't even touched on sets, which in CL are not a built in type, even though there are functions that let you pretend a list is a set, it's not really the same thing when it's not an efficient underlying representation. Personally I wouldn't call…

I don't know car/cdr/cadr/caar etc might not be close to the current machine architecture... But to me at least it feels quite low level. Perhaps similar to some of the unfortunate parts of the jvm/byte code spec that held back/convoluted dynamic languages on the jvm for a long while.

Well I guess car and cdr literally were CPU instructions back in the day, but it doesn't really make you a low level language in the way that C is. i.e: The entire language is really a thin wrapper over the assembly language which itself is an abstraction of the byte code. Most languages have arithmetic and more advanced mathematical operators that reflect actual op codes in the cpu, but that doesn't make those low level either.

Re: Practical Common Lisp (2009)

#70
post #64

Earlier quoted context omitted.

I don't know car/cdr/cadr/caar etc might not be close to the current machine architecture... But to me at least it feels quite low level. Perhaps similar to some of the unfortunate parts of the jvm/byte code spec that held back/convoluted dynamic languages on the jvm for a long while.

Well I guess car and cdr literally were CPU instructions back in the day, but it doesn't really make you a low level language in the way that C is. i.e: The entire language is really a thin wrapper over the assembly language which itself is an abstraction of the byte code. Most languages have arithmetic and more advanced mathematical operators that reflect actual op codes in the cpu, but that doesn't make those low l…

The expectation is still that cons cells and the operators CAR and CDR can be implemented very efficiently. A CONS cell will on a typical machine be just two machine words - on a Lisp Machine it was sometimes just one machine word - when the cons cell was a part of a linear list.

If you look at an unsafe CAR operation, it is often just a single machine instruction.

Take this function which calls FOO twice. FOO is just calling CAR and inlining this call.

  (defun bar (a)
    (declare (optimize (speed 3) (safety 0) (debug 0))
             (type cons a)
             (inline foo))
    (the cons (foo (the cons (foo a)))))
The disassembly then shows that CAR is a MOV instruction:

  ; Size: 14 bytes. Origin: #x1002FB5E49
  ; 49:       488B40F9         MOV RAX, [RAX-7]                 ;  no-arg-parsing entry point
  ; 4D:       488B50F9         MOV RDX, [RAX-7]
  ; 51:       488BE5           MOV RSP, RBP
  ; 54:       F8               CLC
  ; 55:       5D               POP RBP
  ; 56:       C3               RET

For a language like Lisp this is low-level stuff: telling the compiler to optimize for speed, no runtime checks, provide no debug information, declare the type of a variable, declare return types and instruct the compiler to inline a function.

Or take structures in CL. They are defined such that can be compiled to a linear vector-like data structure with slot-access to known and static offsets. No redefinition, no meta-data information - low overhead.

Two of the original goals of Common Lisp were defined as this:

> Portability

> Common Lisp intentionally excludes features that cannot be implemented easily on a broad class of machines. On the one hand, features that are difficult or expensive to implement on hardware without special microcode are avoided or provided in a more abstract and efficiently implementable form. (Examples of this are the invisible forwarding pointers and locatives of Zetalisp. Some of the problems that they solve are addressed in different ways in Common Lisp.) On the other hand, features that are useful only on certain ``ordinary'' or ``commercial'' processors are avoided or made optional. (An example of this is the type declaration facility, which is useful in some implementations and completely ignored in others. Type declarations are completely optional and for correct programs affect only efficiency, not semantics.) Common Lisp is designed to make it easy to write programs that depend as little as possible on machine-specific characteristics, such as word length, while allowing some variety of implementation techniques.

> Efficiency

> Common Lisp has a number of features designed to facilitate the production of high-quality compiled code in those implementations whose developers care to invest effort in an optimizing compiler.

Post reply on HN