I went through HtDP awhile back. Mind-blowing, changed the way I approached programming, yada yada. Scheme held a special place in my heart for years after.
Back then I would have loved to have found a job where I could have used it. I settled for Ruby. After a year of using Ruby seriously, I started to wonder again about using Scheme, so I took a look at some of the available options.
Naturally, like everyone else in this situation, I ran into the brick wall of just not having the community support Ruby has, so it's very difficult to justify using it on any project except purely personal projects, and even then, it's more about exploring a domain rather than exploring a programming paradigm, so I never did get to do anything serious in Lisp. Clojure's nice and all, but the JVM is turning into quite the bit of baggage, hard to justify using it over Ruby.
That's when I took another look at Ruby and realized it did pretty much everything I needed, meta-programming-wise, why bother with continuations and macros when you can use composition and module mixins to achieve practically the same thing? (environment tracking and easy DSL creation) Sure it's not as mathematically pretty as Lisp is, but Ruby syntax is still much prettier than Lisp's.
After awhile I realized that the HtDP way of solving problems and the OO/SOLID way work equally well. You'd just use one with a language like Ruby and the other with a more functional paradigm like Lisp. OO is plenty flexible if you avoid listening to the zealots.
One focuses on the functions/data and the other focuses on the classes/objects. Screw that, I say focus on the domain. Make that logic as tight as you can, then introduce interfaces to it. With that model, Ruby works Just Fine.
No. Lisps have no syntax at all. You write your programs in AST form. It's not possible to compare this to writing Ruby or Python. For the comparison to be fair you'd have to write Python like this:
I don't get at all how you say that to get a decent comparison, you have to write your Python/Ruby in AST form. That's just silly. Any language requires multiple transformations before it can be run by the processor, Lisp is no different.
S-expressions are definitely a syntax, that's close to the AST it parses into, but not quite. You still need a parser, because there's still quite a bit of ambiguity that gets introduced by language features like macros.
What's the point of printing out Python's AST and saying it isn't readable? It's not designed to be read by humans!
That last bit would look like this in idiomatic Ruby:
(1..100).map {|x| x ** 2}
... which I think is far more readable than your Lisp, almost half the characters, and it translates its intentions better, by putting the more important information first, the values of the range being mapped, and compactly representing the predicate function. You can omit the lambda keyword entirely because syntax allows you to denote one in a much more legible way. Can't do that with Scheme, there's no way to denote an inline function without it. Guess what, that's syntax.
S-expressions sacrifice readability because not every programming idea needs to be expressed exactly the same way. applying a lambda function should not be expressed in the same way as passing a value as an argument. They're two completely different actions, they solve two different problems, and that should be reflected in the syntax. Otherwise you have to read the code more closely to try to figure out what it's doing.
And what you're gaining, homoiconicity, is a neat trick, but I've learned that it isn't really necessary for anything. I do find myself wanting it whenever I'm doing something really abstract like AI programming, but it's not like I really need it or anything. You can easily use language-specific meta-programming to solve the same problems, and the learning curve for that is a bit steep, but it will make you a better coder in the end.
For data. s-expressions are not a syntax for Lisp. Only for data.
> You still need a parser, because there's still quite a bit of ambiguity that gets introduced by language features like macros.
Macros introduce no ambiguity. They are destructuring and/or transforming the code.
Much less noise than your Ruby example:
(mapcar 'sqr (in-range 100))
(loop for n from 1 to 100 collect (sqr n))
Lisp as a programming language is based on words, tree structure and indentation, not so much on special characters. That's different from Ruby. But reading and parsing words is not really different to learn, otherwise you would have problems reading this text, which has lots of words and few special character.
> For data. s-expressions are not a syntax for Lisp. Only for data.
Such a strange statement! Code IS data in Lisp.
> Macros introduce no ambiguity. They are destructuring and/or transforming the code.
And just what do you think syntax rules do? Any language could be represented with s-expressions. They just choose not to because s-expressions are a pain in the ass to read. The earlier example mapping square over a range expressed the exact same idea, so the representation is identical. You could write code to transform Ruby into s-expressions the same way Coffeescript compiles to Javascript. Nobody has yet, guess why?
> Much less noise than your Ruby example:
One could argue that the glut of parentheses are themselves noise. There are other ways of structuring Lisp programs that don't use them, but rather a one statement per line syntax like every other language. Also that prefix notation isn't natural for humans to read, dot notation is much more intuitive, which is why it's used in so many languages. Also that the special characters in, say, Ruby's block syntax reduce noise by providing visual cues to what parts of your code are what.
But whatever, if you think
(1..100).map {|x| x ** 2 }
is noisy, then there's really no point in arguing with you, because we see the world two totally different ways.
And this:
(loop for n from 1 to 100 collect (sqr n))
...looks exactly like a Ruby-style DSL implemented in Lisp. If I saw that in someone's code I'd be like, WTF is that doing? A method with 8 parameters? Oh wait, it's a macro. Why use a macro for that, what's wrong with building the range with a function instead of reimplementing BASIC in Lisp? Isn't the whole point to get away from imperative style?
let ({var | (var [init-form])}*) declaration* form*
Both syntax definitions are violated.
Obviously, the syntax of s-expressions is not enough to describe the syntax of Lisp. Far from it. S-Expressions just describe a textual syntax for data: symbols, numbers, strings, lists, cons cells, ... But it does not describe the syntax for defining functions, defining variables, exception handlers, function calls, etc.
> They just choose not to because s-expressions are a pain in the ass to read.
They aren't.
> Isn't the whole point to get away from imperative style?
Lisp makes no commitment to a particular style. 'imperative' is just fine. As is functional, object-oriented, rule-based, ...
I saw one in the form of a wiki awhile back. schemers cookbook maybe. The venerable (lambda-the-ultimate) and readscheme still excellent. We can always use more lisp and scheme oriented sites.
Back then I would have loved to have found a job where I could have used it. I settled for Ruby. After a year of using Ruby seriously, I started to wonder again about using Scheme, so I took a look at some of the available options.
Naturally, like everyone else in this situation, I ran into the brick wall of just not having the community support Ruby has, so it's very difficult to justify using it on any project except purely personal projects, and even then, it's more about exploring a domain rather than exploring a programming paradigm, so I never did get to do anything serious in Lisp. Clojure's nice and all, but the JVM is turning into quite the bit of baggage, hard to justify using it over Ruby.
That's when I took another look at Ruby and realized it did pretty much everything I needed, meta-programming-wise, why bother with continuations and macros when you can use composition and module mixins to achieve practically the same thing? (environment tracking and easy DSL creation) Sure it's not as mathematically pretty as Lisp is, but Ruby syntax is still much prettier than Lisp's.
After awhile I realized that the HtDP way of solving problems and the OO/SOLID way work equally well. You'd just use one with a language like Ruby and the other with a more functional paradigm like Lisp. OO is plenty flexible if you avoid listening to the zealots.
One focuses on the functions/data and the other focuses on the classes/objects. Screw that, I say focus on the domain. Make that logic as tight as you can, then introduce interfaces to it. With that model, Ruby works Just Fine.