Hacker News new | ask | show | jobs
by camdez 1211 days ago
I certainly don't fault you for having that reaction, but I would encourage you to look deeper and not dismiss one of the most interesting families of programming languages on the basis that it looks weird. Far from atrophying your skills in other languages, many people say that learning Lisp makes them better programmers even when they're coding in other languages.

Lisps don't arbitrarily look weird—there's a deep, principled, elegant reason for it; Lisp code represents how the code will be evaluated in the most direct way, without relying on (some would say needlessly) complex parsing / precedence rules. There are no surprises and no arbitrary rules to learn. There are no useless semicolons to forget, and you'll never have to wonder if `+=` returns the RHS or the result of the operation (or does it even have a return value?).

You don't have this meaningless distinction where you can't directly reduce with `+` because—ugh—it's not a function, it's an operator. You just say `(reduce + [1 2 3])`.

You never have to do this ugly Ruby stuff...

  words.map &:length
  # or
  words.map { |w| w.length }
...because methods are really just polymorphic functions, but language designers chose syntax that doesn't compose elegantly.

You don't have this useless distinction between statements and expressions that limits how you can compose code. You never have to drop down to some ugly, limited tertiary expression form (`COND ? X : Y`) of `if` because—whoops—`if` is a "statement". You just write:

  (println (if me? "me" "you"))
Because, duh, we wanted an `if`.

What do we gain by adding all of this noise?:

  if (is_me) {
    println("me");
  } else {
    println("you");
  }
Absolutely nothing. The parens on the conditional, the curly braces, the semicolons, the `else` keyword—they're essentially meaningless incantations to appease the compiler. And we've introduced an undesirable opportunity for the two branches to accidentally diverge over time.

But most importantly, our code is written in the data structures of our language. Code as data means we can manipulate code as easily as we manipulate data, which means we can trivially (re-)write code with code (i.e. macros). And not shitty string generating macros, or macros that can only do a handful of sanctioned things—we can write our own control structures in couple lines of code. We can add new abstractions to the programming language from user space.

Wish the language had an `if-not` construct? You can add it with, like, 3 lines of code. Wish functions could have optional parameters? Add it. Wish it had a pattern matching functions like SML or Erlang? Cool. Java-style annotations? Logging that is fully removed when running in high performance mode? A different OO model? Multi-line string literals? String interpolation? A graph of dependent calculations that only get run when used? A more convenient way to load dependencies? It's all easily doable.

I've coded in Lisps (and a dozen other languages) for at least 20 years, and every time I have to use a non-Lisp syntax I just think "wow, these people really missed the boat". It's like having to write math in Roman numerals (would you rather calculate "D + L + IX" or "500 + 50 + 9"?); there's a better way, and that better way has elegant, recursive, underlying design principles that make the ergonomics way better.

But, yeah, it doesn't look like C code. And people seem to be really attached to their C syntax.

1 comments

That example is quite unfair. You could just write

  println(is_me ? "me" : "you")
which has exactly the same amount of symbols.
I specifically called out the tertiary operator in my text so as to be fair, but tried to keep things simple in my example. Perhaps too simple.

Of course you can do that simple case with a tertiary operator but:

1. It's a construct that really has no reason to exist (I argue) as distinct from `if`.

2. It doesn't compose with statements.

This duality is primarily what I'm arguing against.

A better example would have been a case statement inside of the `println`:

  (println
   "Log in by"
   (case user-id
     0 "root"
     1 "local admin"
     (format "regular user (id: %d)" user-id)))
In C, you have to introduce a variable for no good reason (or do some non-idiomatic, ugly, nested tertiary operators that get uglier the more cases we have).

And even then, you can't just say

  user_name = switch { ... }
Because switch is an statement.
I mean sure, more things being expressions instead of statements is pretty nice (e.g. in Rust you can do that). But you don't need to introduce different syntax for that.