Yeah, replying to myself. I'm not sure if that's considered weird but ok.
According to this logic, most mainstream languages don't make sense. Notably, Java, C#, C, C++, Clojure are all screwed up. Not sure about other lisps. Ruby, PHP, JavaScript, Python, F#, Scala and Haskell have it right though.
(yeah sure, you can do mutability in Scala and immutability in Java, but the languages and their community lean toward the other, which is what matters here, I suppose)
Given that I've been much of a C# fanboy recently, my nose bleeds.
> it feels like Clojure went all liberal on one end, just to get super-conservative on the other.
In the brief time that I used Clojure, I also thought that the contrast between immutability and dynamic typing was very strange. Ultimately, I think it makes more sense than having the entire language be highly dynamic (or highly static).
I think Clojure's choice is not inconsistency, but that there is a kind of budget for craziness in a language. In Clojure, you can go crazy with dynamic types on the foundation of immutability, transactions, etc. In Haskell, you can go crazy with really awesome static typing tricks that would be utterly unfathomable if the language wasn't very rigid in every other way -- immutable, referentially transparent, side-effect-tracking. These two pack all their craziness into one corner of the language.
On the other hand, other languages -- Python, Javascript, Ruby -- seem to spread the craziness (I am not sure I picked the best word for this!) around, instead of having one super dynamic feature and everything else very inflexible. These languages tend to lack the really big flashy features: macros, typeclasses, etc.
It seems that you have a lot of flexibility as a language designer on how you spend your craziness budget, as long as you don't go too high (and become incomprehensible) or too low (Java?).
Maybe constraints rather than craziness? Constraints tailor a language, make it fitter for one purpose or another. But you have a limited constraint budget - overspend and everything is difficult.
As an old boss of mine (hi Paul Earwicker) used to say - "flexibility is just design decisions you haven't taken yet."
Craziness is not zero sum game, but it's hard to convince people that complain about "expression problem", "modular abstractions", "better DI", all the phrases to capture part of the problem, to read Real world haskell or Scala in Depth, both demanding books.
jerf did a good writeup, maybe a little overboard on clispscript, but besides that
Clojure's dynamic typing is somewhat different from other languages, though, as it really just has two non-atom types: seqs for collections and maps for data objects. Virtually all collections and objects can be manipulated as one of these two types. So "calling a method" on an inappropriate type is basically passing an inappropriate map to a function. You might want to disallow it, but Clojure allows it for good reason: this makes it possible to have many data access/manipulation functions that work on all objects. So the question is, do you want to forbid passing an argument that doesn't make sense to a function or open the door to lots of useful functions that would work on all objects. Clojure simply chose the latter.
> this makes it possible to have many data access/manipulation functions that work on all objects.
This is not actually true. Add proper type inference and structural typing, and it's perfectly possible to have all those generic data manipulation functions while also having strong static typing.
I'm talking about structural static typing. At runtime, all the types would be fully determined, so no need for reflection. (Or vcalls for that matter).
I think of it as each language making functionality trade-offs, in their design, for specific goals. Each language tweaking the recipe to make a language that best fits the programming model they advocate.
According to this logic, most mainstream languages don't make sense. Notably, Java, C#, C, C++, Clojure are all screwed up. Not sure about other lisps. Ruby, PHP, JavaScript, Python, F#, Scala and Haskell have it right though.
(yeah sure, you can do mutability in Scala and immutability in Java, but the languages and their community lean toward the other, which is what matters here, I suppose)
Given that I've been much of a C# fanboy recently, my nose bleeds.