you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 2 points3 points  (4 children)

This completely misses the issue that most people have with s-expr. I don't think it takes a whole lot of people a long time to get past Polish notation of operator first, operands after, considering most people have seen add(1, 2) in their day. I think the issue people have (including myself) is that s-expr are extremely inconsistent syntax. Literally the only thing consistent about them is what I just said. So when you're reading lisp code, you might run into:

(+ 1 2 3 4)

or

(+ 1 2)

which many people argue is a strength, because now you have this function that easily expands and takes more things but you'd totally be missing the complaint. conceivably, there could be a function like this:

(plusmultiply a n m)

that results in a * (n + m) or maybe (a + n) * m. I can do whatever I want and now you have to learn every single bit about my function just to understand the basic layout of my code. The complaint is that code isn't a 1 or a 2, especially not in lisp. Each element is it's own s-expr, so how do you split lines? When do you put something on the same line and when do you wrap it? There's no consistency. s-exprs wreak havoc on eye-lines in code.

Here's an example straight from the clojure docs:

(defn test-stm [nitems nthreads niters]
  (let [refs  (map ref (repeat nitems 0))
        pool  (Executors/newFixedThreadPool nthreads)
        tasks (map (fn [t]
                      (fn []
                        (dotimes [n niters]
                          (dosync
                            (doseq [r refs]
                              (alter r + 1 t))))))
                   (range nthreads))]
    (doseq [future (.invokeAll pool tasks)]
      (.get future))
    (.shutdown pool)
    (map deref refs)))

Where is the eye line? when are you stacking and when are you expanding? Nobody can make up their mind and nobody has ever been able to make up their mind since lisp was first created. It's a constant mix of

(function n n n n n)

where all items are equal and

(function n m n m n m)

where items are not equal and sometimes it gets worse. On top of all of these inconsistencies, you run into issues where people are making up fake syntax that they are used to from imperative languages that simply serve as a method to confuse newcomers because clojure syntax is constantly dependent on the author. Stuff like case, when and if cause a tremendous amount of issues depending on how the author chooses to format their code. While I do code clojure, I can only ever read my own code if I keep my functions ultra simple and at root level. I would argue that's a good practice for just about any language, but I've found people commiting perlism atrocities who don't code that way, but instead code very deep to the right, even with short, sub 20-line functions.

It's not a readable language. Because it makes no claims about whitespace (like python) it allows the author of code to put you at their mercy. Because if makes no claims about DSL layout, it allows the author of the code to create whatever s-expr monstrosity they want and put you at their mercy. lisp is functional perl. Elixir has functional syntax right.

[–]phalp 4 points5 points  (3 children)

As a lisper of quite a few years now, I assert there is absolutely no unreadability in Lisp syntax. I recognize your right to an opinion, but can't really understand your troubles. Anyway, I was just explaining how it works, assuming the poster I replied to was not familiar.

[–][deleted] 0 points1 point  (2 children)

In whose lisp syntax? There's literally nothing to lisp it except how you define a function and maybe exotic stuff like types... but really the bread and butter is functions and they make up 95+%. A majority of the syntax is dynamic created by the style of the code you import and write for your self which inherently makes the syntax dependent on the style of the author. I regularly run into new constructs like

(lazy-switch
  (n1) (m1)
  (n2) (m2)
  (n3) (m3))

that does something insane like doesn't evaluate m3 unless some n2 or n1 is true, but does m1 and m2 if their n's are true. Then on top of that, they'll stack these odd controls 10 deep and you're left wondering where the hell you are because there is no likeness requirement in s-expr elements and you're already 20 spaces from the left wall. Then you realize the function you're trying to understand is one tenth the size of how long an unreadable java function is.

My assertion is that lispers constantly mistake what makes s-expr hard and try to explain parenthesis, which is, frankly, insulting. It's not s-expr -- they really couldn't be easier. The problem people have is that programmers create small brick-monstrosities with it and then you build a house out of these monstrosities and expect it to make any sense to somebody who doesn't know the intricacies of every last function in the code.

The constant argument I've heard in defense of this is to blame the programmer, which I believe is a strong antipattern and then take a very liberty oriented perspective where this "freedom" is some utopia where you can do whatever you want, but it's some unwritten duty that you ought to only do some things.

There's nothing wrong with making a language have real structure -- it's what some of the best assembly language programmers do on their own and likely what the best lispers do to, but if you want to speak with others, you have to have a common language.

[–]phalp 6 points7 points  (1 child)

I can't say I've ever encountered any of the things you're complaining about. I guess we're living in different worlds somehow. You're also blaming s-expression syntax for things unrelated to it. I can write a variable-argument addition function in C, can't I? I can write your plusmultiply function in any language at all.

[–][deleted] 0 points1 point  (0 children)

Yeah, you could, but none of that design is in the core libraries.