What brought me to that conclusion was my rejection of Scheme. I learned Scheme first, through HtDP, which absolutely blew my mind. I wanted to use Scheme for everything, but instead made the pragmatic choice to learn Ruby so that I could more easily find a job, and just use HtDP concepts in my Ruby programs.

Well after awhile I realized I didn't miss Scheme. Like, at all. I had to really think hard about why. The first insight I had was that, well, syntax really does matter, and the paren-soup just isn't aesthetic.

The second thing I realized was that Ruby's object model is just far more intuitive than any other way of dealing with objects. It's just, well, elegant. Everything's an object, everything has a class. This is what Crystal misses. And it can't because of its static nature, types can't be a class. A certain kind of reflection is impossible.

And this is where static vs. dynamic comes in. Static forces you to write boilerplate. Dynamic lets you write it when you need it. You want dynamic until you want static. It's just that simple. No one is inherently better, they're just better suited to different situations. You want to flow from one to the other, not get handcuffed. This makes dynamic inherently better, because you can build a static type checker on top of a dynamic language, but to put dynamic typing inside a static language is just always going to be a kludge.

If I can get pleasing syntax, an elegant object model, and gradual typing in a language that's also faster than Ruby, I'd jump on it. But every other language makes compromises on these points for one reason or another. Of all three, it seems the object model is the most difficult to implement, but it's one I rely on to get me out of jams. I don't often need metaprogramming, but when I do, it saves me a lot of time. And I've never run into anything else like it save in the Lisps.

If I didn't have Ruby, I'd be using a Lisp. Having metaprogramming available, even if you're not using it, gives a real sense of safety, not against silly bugs, but against the much bigger possibility, of making poor abstractions. In Ruby, once I've realized I've made a bad abstraction, I can take the working code and metaprogram it, keeping behavior working, until it's using the semantics I need from it. This makes me far more confident to just knock up systems as quickly as possible, safe in the understanding that I can turn any semantics into any other semantics when I need to. You can fix the propensity to make type errors in a dynamic language with discipline, but you can't fix the propensity to make wrong abstractions with any amount or kind of discipline that I've ever been able to see.

But I'm also surprised just how much syntax impacts my desire to actually write code. There's something about Ruby's code block delimiters that are more pleasing than either whitespace or bracket delimiters. I don't mind bracket delimiters if the whole block is on one line. But to force me to use brackets for multi-line code blocks, the so-called C syntax, well I don't find it half as aesthetically pleasing as Ruby. Is it just personal preference? Perhaps. But I think a hundred years is going to really clarify everything and languages will lose their utility advantages over each other and so all that's left will be aesthetics. And I think Ruby just has everybody beat in this area.