5 ms·
I'm a static typing lover. My two favorite languages are Standard ML and Haskell, which I have found very well suited to the way I think: denotationally (expres
by catnaroek 11y ago
I'm a static typing lover. My two favorite languages are Standard ML and Haskell, which I have found very well suited to the way I think: denotationally (expressions are given meaning by a mapping to a semantic domain), algebraically (substituting variables for expressions), abstractly (selectively ignoring implementation specifics) and structurally (the only operations allowed on an expression are those sanctioned by its type).
However, I don't think Scala supports your argument very well. Its type system is:
(0) Redundant: When should I use an abstract class or a trait? When should I provide extended functionality using a subclass or an implicit? What are the relative benefits of poor man's ML-style type abstraction (with path-dependent types) vs. poor man's type classes (with implicits)?
(1) Inconsistent: Why are closed sums [(generalized) algebraic data types] implemented in terms of an open-ended inheritance mechanism? This makes `match` exhaustiveness checks unnecessarily more complicated, and hiliarity ensues when the implementors get it wrong.
(2) Unhelpful: Scala's type inference is local and unidirectional, from arguments to results, which limits the usefulness of using types for exploratory programming. GHC Haskell, which has all sorts of crazy type system extensions (some even more ambitious than Scala's), does a much better job of globally inferring types.
So, in spite of my general preference for statically typed languages, I find Clojure easier to live with than Scala, simply because Scala has so many warts.
- paulddraper 11y agoWell, against the standard of ML, Scala does come up short. Almost all of that is due to the intentional choice of running on the JVM with high interop compatibility. Choose ML-family or Haskell, and you don't have Java baggage. But I'd take types with class-vs-trait design choices, less-than-ideal match exhaustiveness checks, and type inference incompleteness any day over a language lacking all static typing features.
- catnaroek 11y ago> Almost all of that is due to the intentional choice of running on the JVM with high interop compatibility. Here are some counterexamples: (0) Clojure is compatible with Java, but has less warts than Scala. It is not statically typed, but it is internally more unified and consistent. With some exceptions, you only get to see ugly compatibility compromises when you need to interact with Java code. (1) Rust is compatible with C - in fact, it interoperates with other languages that have C FFIs by simply pretending to be C. Nevertheless, outside of specifically designated `unsafe` blocks, which idiomatic Rust only uses sparingly, most code is written in a "modern C++ meets ML" style. > But I'd take types with class-vs-trait design choices, less-than-ideal match exhaustiveness checks, and type inference incompleteness any day over a language lacking all static typing features. Static typing is a useful feature, but I think it's narrow-minded to use it as the only criterion for judging a language's merits. For instance, Clojure's macros are objectively nicer than Scala's, even if both pale in comparison to Racket's. Writing multimethods in Clojure is straightforward once you know the syntax, whereas emulating multiparameter type classes in Scala can be confusing unless you know the specifics of how implicit parameters are resolved. (This is, ironically, a direct consequence of Scala's richer type system.)
- paulddraper 11y agoWhen I said Java interop, I meant two-way. With few exceptions (traits being the only I can think of) all Scala code can be used by Java. You can even use implicit parameters, though of course the Java compiler won't resolve them for you. And almost all Java constructs can be created with Scala (Java enumerations being an exception). For example, using Scala you can define an abstract class that Java code can use. You can't do that in Clojure. Real world examples: Play and Akka are two large projects that include Java development in their primary use cases. Two-way interop is the cause for a huge number of less than ideal decisions: different Array equality than Seq, @specialized, universal equality, hash codes, and toString, abstract class vs trait, generic type erasure, etc. You can call the compromise misguided (and you wouldn't be the first to do so) but that's why.
- catnaroek 11y agoWhy is there a need for a one-to-one correspondence between Java and Scala features? In any case, you can call Clojure code just fine from Java. Of course, the API won't look like one a Java programmer would've designed, but that's okay - Clojure isn't trying to be Java. It's trying to leverage the JVM environment.
- daxfohl 11y agoTake twitter for example. They do a ton of stuff in Scala but make everything straightforward enough to use from Java that you wouldn't know it was Scala. This is fairly trivial in Scala but difficult in Clojure.