Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

Lisp Considered Harmful

Name: Anonymous 2013-04-07 16:35

Some of this criticism is related to Common Lisp and was fixed in Scheme or Racket. So it is mostly about why you should prefer Racket to Common Lisp or Clojure.

1. CONS-pairs simplicity is deceiving: CONS introduces mutability, together with silly notions of proper and improper lists, while impeding advanced and optimized list representations, so you can't have lists with O(log2(N)) random access, catenation and insertion, like Haskell's finger trees.
2. NIL punning (treatment of NIL of as empty list, false and void) breaks strong-typing and goes against lambda calculus, which hints us that IF should accept only TRUE and FALSE, which should be functions (Church Booleans), so all objects must be explicitly coerced to boolean for use with IF. Common Lisp has a confusing lot of NIL/CONS related predicates: there is CONSP, LISTP, ATOM, ENDP and NULL, while TYPECASE discerns between LIST, CONS, BOOLEAN and NULL. The ATOM predicate considers NIL an atom, so if you have some code that should be invoked on every list, you can miss some empty lists. Hash-table access returns NIL, when key isn't present, making it harder to store empty lists. Some Lisps, half-broken behavior: for example, Clojure, discerning between nil and empty-list, still allows using nil in-place of empty-list; moreover, Clojure introduces true and false, duplicating nil and confusing semantics further. NIL punning is the single worst Lisp wart, reminiscent of PHP and JavaScript horrors.
3. Non-Lispy behavior: FORMAT function competes with regular expressions in providing cryptic and unreadable DSL, indulging obfuscated code like "~{~#[<empty>~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~:}", which could have been easily replaced with SEXP based format. On the other hand, Lisp misses string construction, like "Name=$name, Age=$age", so you have to carry FORMAT everywhere. LOOP macro employs overly complex Pascal-like baroque syntax with a lot of clauses, having complex interplay with each other, which would be confuse newbies and annoy Lispers, who love simpler solutions, playing nicely with lambda calculus, like letrec.
4. Over-engineered OOP: despite Lambda Calculus prescribing using lambdas to construct objects, most Lisps implement full blown class-based OOP, like CLOS, conflicting with Lisp's minimalism and complicating semantics. Moreover, lists/conses and booleans don't have first class citizen rights in these ad-hoc object systems, meaning you can't overload CAR or CDR, so you can't implement your own lists, like, for example, a list-like interface to filesystem, where a directory could behave as a list of files and every file as a list of bytes. Despite CLOS generics looking like functions and cluttering global scope, you can't pass them to MAP or other high-order functions. Package symbols have dynamic scope, which is argumentably bad and shouldn't be indulged, while packages are global and first-class, so you can't easily get a sandboxed environment (like Unix's chroot) by making a package with only safe functions. Instead of providing viable encapsulation, some Lisps treat the symptoms by introducing second namespace for variables, so that identifiers would have less chances to collide. Other Lisps, like Clojure, disapprove OOP, using some ad-hoc package systems and kludges, like "protocols".
5. Duplication is not the right thing: CONS duplicate arrays and lists; QUOTE duplicates QUASIQUOTE, which for some reason implemented as reader macro, so you can't overload it for you own use; LOOP duplicates DO; symbols duplicate strings; chars duplicate single-letter symbols. Package encapsulation duplicates OOP, which in turn duplicates encapsulation features provided by lexical scope. Growing from broken OOP encapsulation, there is an explosion of comparison functions, just to compare for equality we have: =, char=, string=, eq, eql, equal, equalp, tree-equal, string-equal, char-equal; worser, `eq` compares pointers and has undefined behavior, so it should be part FFI, instead of being exposed with normal interface. Analogously, AREF, SVREF, ELT, NTH, CHAR, SCHAR, BIT, SBIT - all do exactly the same.
6. Verbosity: define and lambda are most used keyword, yet take 6 character each; then we have monstrosities like destructuring-bind and remove-if-not, which could be named just bind and keep. Verbosities like MAKE-HASH-TABLE, MAKE-ARRAY and (DECLARE (INTEGER X)) ensure that you will avoid optimized structures and type-checking at all cost, making your code slower. In rare cases, when names ain't verbose, they are just cryptic, like PSETF, CDAADR and REPLACA. Macros LET and COND have especially bloated syntax, where simpler (let name value …) would have been enough, LET adds additional 2 levels of parentheses (let ((name value)) …) just to annoy you. CLOS and defstruct syntaxes are especially verbose, which is aggravated by absence of self/this context. Many people complain about absence of infix expressions, which could have been easily supported through reader macro, like {a*b+c}, and while Scheme does support infix expressions, it misses operator precedence, making it incompatible with formulas you may copy-paste from your math textbook. LISP lefts unused a lot of special characters, which otherwise would made code succinct and provided visual cues: for example, you have to write (list 1 2 3), instead of [1 2 3].
7. Missing features: While Lisp does support complex and rational numbers, it doesn't support vector arithmetic, so you have to write something like (map 'vector (vector x1 y1 z1) (vector x2 y2 z2)), making any 3d graphics exceedingly verbose. A few important functions, like SPLIT and JOIN, are missing from standard library, which for some reason includes rarely used string-trim and string-right-trim, although JOIN usually simulated with  (format nil "~{~a~^-~}" '("a" "b" "c")), making code impenetrably cryptic. Absence of good type system, call-by-name (lazy evaluation) and immutability, which really makes functional programming shine. Although, Qi does provide acceptable type system and Clojure introduces immutability, we can't have all this in a single production quality Lisp. Call-by-name is more of an on-demand-feature to be used in some contexts (like implementing if/then/else special-form), but no Lisp features it, despite call-by-name being natural semantics of Lambda Calculus. Some popular Lisps, like Clojure and Common Lisp, don't even guarantee TCO (tail call optimization), meaning that expressing advanced control structures would be hard. No Lisp, beside Scheme, supports continuation - silver bullet control-flow feature, although some Lisps do support goto - limited form of continuations. "It needs to be said very firmly that LISP is not a functional language at all. My suspicion is that the success of Lisp set back the development of a properly functional style of programming by at least ten years." -- David Turner.

Name: Anonymous 2013-04-10 4:31

>>40
Because the past and languages from the past are superior.

Name: Lambda Arthur Calculus 2013-04-10 10:51

despite Lambda Calculus prescribing using lambdas to construct objects
I NEVER DONE DAT SHIT YA FUCKIN LIAR

Name: Anonymous 2013-04-10 10:53

>>1
I TOLD A BUNCH OF NOOBZ TO READ DA FUCKIN STANDARD BUT I AINT GOT NOTHING ON UR OOP SHIT

Name: Anonymous 2013-04-10 10:55

U CAN POOP UR OOP OUT A FUCKIN WINDOW FOR ALL I CARE

Name: Anonymous 2013-04-10 11:11

>>44
You can't defenestrate OOP.

Name: Anonymous 2013-04-10 11:15

defenestrate a shit

Name: Anonymous 2013-04-10 11:16

>>45
I AINT TALKIN BOUT DEFENESTRATION U FUCKIN NOOB, I'M TALKIN 'BOUT POOPIN OOP OUT A WINDOW. REED DA FUCKIN STANDARD DEFINITION FOR DEFENESTRATION U FUCKIN NOOB. "THROWING SOMETHING OUT A FUCKIN WINDOW". I'M NOT TALKIN BOUT THROWIN SHIT OUT A WINDOW, I'M TALKIN BOUT POOPIN IT OUT A FUCKIN WINDOW. N DESE SONS OF BITCHES CAN POOP DER FUKIN OOPS OUT ALL DA FUCKIN WINDOWS DEY WANT. THINK IT'S IMPOSSIBLE? WELL I'LL JUST POOP AN OOP IN YOUR FUCKIN LUNCH THEN U RETOID N WE'LL SEE HOW FUCKIN IMPOSSIBLE IT IS.

Name: Anonymous 2013-04-10 12:16

Racket is slower than sbcl.

I'll pass

Name: Anonymous 2013-04-10 12:56

>>48
And SBCL is slower than GCC, which is slower than assembly.

The slowest part of Racket is it's FFI and the fact it has no type declaration for unboxed arithmetic.

Name: Anonymous 2013-04-10 13:18

Name: Anonymous 2013-04-10 17:08

nice comparison show why Racket is better than Clojure:
http://hyperpolyglot.org/lisp
Racket: (rest '()) = error; (cdr '()) = error
Clojure: (rest '()) = (); (next '()) = nil

Name: Anonymous 2013-04-10 17:37

>>49
Racket isn't higher level than sbcl.

Name: Anonymous 2013-04-10 17:38

well except for call/cc I guess.

Name: Anonymous 2013-04-10 17:42

>>49
Oh so the slowest part of racket is one of tge most important? Thanks for giving me more reason to not use that shit.

Name: Anonymous 2013-04-10 17:45

>>49
C and ASM lose it with the amount of code and time i have to put into a project. Hence me using a higher level language to begin with.

In any manner the lisp dialect battles are silly becausethe ruby is better than all of them in all regards

Name: Anonymous 2013-04-10 17:47

>>51
Both functions should return nil

Name: Anonymous 2013-04-10 17:51

>>53>>54
call/cc doesn't pose any compiler challenges, because CPS corresponds to static single assignment form.

Name: Anonymous 2013-04-10 17:51

>>55
Computers are a thing in themselves and that’s what you get for being a corporate faggot with a deadline.

Programming languages are not mere tools.

I bet you're the kind of faggot who wants everything to be automated.

Name: Anonymous 2013-04-10 18:13

>>57
It's hard to do it well. Sure, you can just use a linked list for the stack and create and link activation frames as you go, but this might not be the best way on a architecture that intends to be primarily a stack machine. Optimizations, like determining what bodies of code don't need to support continuations, which continuation calls are actual delimited continuations, etc, are harder.

Name: Anonymous 2013-04-10 18:17

>>58
Don't you have another toilet to scrub?

Name: Anonymous 2013-04-10 18:22

>>59
most uses of continuations compile to simple gotos.

Name: Anonymous 2013-04-10 18:27

>>61
That would be a delimited continuation called within the same activation frame it was created, but yeah. Using continuations for error handling ends up like that. It gets tricky once you return the continuation to a calling function, and later invoke it.

Name: Anonymous 2013-04-10 19:28

>>62
Implementing C++ exceptions is tricky too. Exceptions are one of the most complex parts of C++, competing only with templates.

Name: Anonymous 2013-04-10 19:29

>>62
Continuations are also faster than setjmp/longjmp and definitely conceptually simpler.

Name: Anonymous 2013-04-10 19:30

>>60
Don't you have another meeting with your boss (wink wink up the ass)?

Name: Anonymous 2013-04-10 20:51

>>63
It's not that hard. If it was hard, it wouldn't have been a part of C++. Like all features of C++, the implementation is easy, but the consequences of its use can be hard to manage. And templates are trivial shit, but better than nothing at all.

>>64
It might be conceptually simpler if you've only programmed in the lambda calculus and see computers purely as reduction machines, but setjmp, longjmp is the fastest way to implement a delimited continuation, which is the least demanding form of a continuation.

>>65
I'm self employed. I live off of dubs.

Name: Anonymous 2013-04-11 5:56

>>66
It's not that hard.
Unwinding is hard, because it requires keeping meta-info for every function. I.e. you basically implement half-assed closures. Then you may as well keep function name just to provide stack traces.

And templates are trivial shit, but better than nothing at all.
Templates require complicated pattern-matcher and a lot of case analysis. From the top of my head, I can't even say how to implement them.

If it was hard, it wouldn't have been a part of C++.
C++ includes RTTI (required for VTBL), RAII, automatic copy constructors and other tricky crap, working behind your back and shooting you in the leg.

And while C++ has static typing, it wastes it by being weakly-typed and treating integer 0 as false. IIRC, Ada doesn't have such type punning. Yet Worse is Better and most retards wont even notice, they were shot in the leg because of this "feature".

Name: Anonymous 2013-04-11 23:41

>>67
It's easier than you think. Implement a dynamically scoped variable, and you are two steps and one long jump away from a complete exception system.

Templates are optimized version of a c technique that would re-include c source files with different values for certain macros. It's not that hard do. The hardest part is parsing C++'s horrid syntax.

As I said, all of C++'s features have a trivial implementation, but create problems when you use them. Some are manageable, while others are bad enough to be banned for the most part in standard practice. This is consistent with the examples you mentioned, so we agree.

Zero being false and other numbers being true is inherited from your favorite assembly language. It only affects coding style, so I don't think it's that big of a deal. Seeples and See don't have enough expressive power for NULL/zero/false punning to be a serious issue.

Name: Anonymous 2013-04-12 0:32

Actually exceptions are complicated by the calling of destructors (and finally blocks if they were in seeples) when scopes exit. So rather than a long jump, the easiest way is probably just returning from each functions and calling the destructors until you get to where the exception is caught.

Name: Anonymous 2013-04-12 1:51

and the returning to the higher scope containing destructor calls or the catching point could be optimized with meta information in the activation frame, like you said.

Name: Anonymous 2013-04-12 2:58

>>66
I'm self employed. I live off of dubs. dubs
Back to the imageboards, please.

Name: Anonymous 2013-04-12 14:33

Name: Anonymous 2013-04-12 15:39

check 'em

Name: Anonymous 2013-04-14 16:38


Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List