4 ms·
> Every time the data model changes, you've got to go through manually and find all the places the code needs changed, and hope you didn't miss anything. Isn't
by RussianCow 11y ago
> Every time the data model changes, you've got to go through manually and find all the places the code needs changed, and hope you didn't miss anything.
Isn't that what tests are for?
- daxfohl 11y agoNo, that's what types are for. Types are exactly for this. They're far less verbose than tests, they prevent compilation completely on error, they're integrated with your code, they're 100% comprehensive, and they don't contain bugs. Contrarily, tests don't work at all for model changes because the test still retains the old model anyway.
- catnaroek 11y agoI agree with your sentiment - why bother testing something that we can get the compiler to prove, once and for all? However, in Java, a typeful model of a program's specification is bound to be: (0) Imprecise, because of the holes in Java's static safety. You can establish a convention that statically unsafe features are verboten, but it will remain just that - a convention. (1) Verbose, because Java can't express even the simplest ideas without using at least 5 classes: Finite case analysis? Visitors. Higher-order functions? Strategies. Persistent data structures? Memento. Things don't get better if we move to a higher architectural level: Embedded DSLs? You need a separate class for each production rule in your abstract syntax. Type-safe database queries? Java has no structurally typed records, so you need to make a custom POJO class for every query that returns different columns. These things are incredibly unpleasant to deal with, and while it might be true that "unpleasant work builds character", I don't think it should come as a surprise that some people say "enough is enough", and seek alternatives that minimize or at least reduce their pain (like dynamically checked conformance to a schema), even at the cost of sacrificing some type safety (dynamic checks can't enforce higher-order invariants).
- paulddraper 11y agoI agree with you that Java is painful. But if "enough is enough", abandoning all types is throwing out the baby with bathwater. You make a compelling argument for languages with type inference and richer typing systems. There are many such languages. For the JVM, there is Scala.
- catnaroek 11y agoI'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.
- paulddraper 11y agoThis. 100 times this. Types are a "proof" to the compiler that your code will run in a certain way. No missed "edge cases", no fuzziness, just pure comprehensive logic. Of course, the success of this varies by language and a lot of people have worked to find better and more powerful approaches. But you should regard any type system as a proof system for runtime behavior. (I'm not saying that running tests with arbitrary examples is bad; I'm just saying that HUGE classes of potential bugs reliably go away with good static typing.)