5 ms·
This article misses the point of what a type system brings to the table. The key benefit isn't catching errors. The benefits are largely discoverability, enabl
by dylukes 12y ago
This article misses the point of what a type system brings to the table. The key benefit isn't catching errors.
The benefits are largely discoverability, enabling better tooling, improving the number of optimizations compilers can make, and guiding the programmer. These are all things we benefit pretty much unilaterally from. The trade off is having to be slightly more explicit (Java and C# haven't helped this, as they insist you sign everything in triplicate, rather than promote type inference).
For functions with sufficiently rich types, there are often only a couple ways to implement the function. Sometimes, there are few enough that a compiler can actually derive it for you.
Having switched between typed and untyped languages repeatedly, I can't emphasize enough just how much rich, strong types contribute to the readability of the code.
And on yet another front, it may only be "2%", but I'm sure most people on here know how it feels to have written a couple hundred lines of code, only to suddenly find something of the wrong type somewhere it shouldn't be... It only takes a fraction of a percentile for a program to be utterly and completely useless.
- snarfy 12y agoReadability is one of the reasons I try to avoid the use of the 'var' keyword in C#. It's useful in object instantiation, e.g. FooFizzBuzzProvider bar = new FooFizzBuzzProvider() vs var bar = new FooFizzBuzzProvider(); But in other instances it hides the underlying type too much, e.g. var query = (from entity in dbContext.Entities join entity2 in dbContext.Entities2.Include( e => e.Entity3) on entity.entity2Id equals entity2.entity2Id select new { e1 = entity, e2 = entity2.Entity3 }); It works. It's nice. But sometimes it's too nice. At some point the returned type needs serialized, or some other issue which requires a strong type. You end up rewriting the dynamic stuff with strong types anyway.
- dylukes 12y agoC#'s var keyword tells the compiler to infer the type. It still has a statically determined type so that shouldn't interfere with type-polymorphic functions such as serialization routines...
- dylukes 12y agoTo expand a little bit: I have found at least anecdotally that the benefits of a strong, rich type system are multiplicative with other features, not additive. For example, types in Java are largely a nuisance. The type system lacks facilities to express obvious things (I want a list of things that all have this interface) in clean ways. Instead you have to resort to "clever" hacks which ultimately just circumvent the guarantees you wanted to establish. For small projects this is a non-issue. For large projects, this is hell. By contrast, type systems thrive in contexts with algebraic or sealed case types, or any form of pattern matching really. Or just plain old enums. In conjunction, these features enable very powerful static checks. Forcing you to handle None/Null/NONE/Nothing/nil cases everywhere encourages critical thinking. This introduces more issues, such as staircase code, but these are largely (I would personally say completely, and with a nice surplus) fixed by things like pipes, monads, computation expressions, and so forth. This extends to libraries as well. I can't count the number of times I've used libraries which changed their API's in "non-breaking" ways, such that certain functions returned types I assumed would never be returned. Was this my fault? Yes. But if the return type had been strongly typed and I had been working in such a language as described above then: 1) I would be forced to handle every extant constructor in that type. 2) If a new one was added, I'd get a compiler warning alerting me to the fact that I hadn't handled it (this is great!). 3) If the type changed entirely, I'd get an error warning me to the fact that my code was no longer compatible with the API provided. In the end, working with and writing libraries is about respecting contracts. Types are a tool for codifying those contracts. Strong typing and matching facilities are even more powerful tools for alerting you to violations.
- tiziano88 12y agoFWIW I don't think the normal syntax for bounded generic types in Java should be considered a "clever" hack, it's just a feature of the language which is actually quite powerful (in spite of type erasure): List of things that all have interface I: List<? extends I> l;
- dllthomas 12y agoI love refactoring in a good, statically typed language. Change the types in a carefully breaking way, and you're immediately told every single place you need to change. Seven times out of ten I can manage to squeeze this out of my C, even before static analysis tools, though certainly wind up leaning more heavily on my tests than in some other languages.
- mseepgood 12y agoI usually have tests for features, regardless of static or dynamic types. You need feature tests anyway, because even sophisticated/complicated static type systems can't catch serious implementation logic bugs. And these tests implicitly cover what a static type system would give you and more.
- dylukes 12y agoI disagree. Unit and feature testing are vastly overrated for what they do, and their current use is faddishly subscribed to in a seemingly cult like fashion. Unit tests are excellent for testing non-stateful behavior of code. That is, they're great for testing contracts, and they have a bit more flexibility than Eiffel style contracts. However, using them to the extent of "returns an Object of this type" is absurdly verbose and pollutes the code base with roundabout information. These sorts of things are best left to a static type system, which is a more specialized and effective system for that portion of validating your code. On the other end of things, tests of more stateful processes or behaviors are better served by E2E testing. There's nothing wrong with using multiple forms of testing. Different tools excel at testing different sorts of things.
- mseepgood 12y ago> However, using them to the extent of "returns an Object of this type" is absurdly verbose and pollutes the code base with roundabout information. I did not talk about unit tests. I do not test stupid things like "returns object of this type". I test functionality (more like integration tests). If the functionality works then things like "returns object of correct type" or "items in collection are treated correctly" are tested implicitly or don't matter. People with ridiculously complex type system programming languages have to write these automated tests as well, because even ridiculously complex type systems like Idris don't ensure the correctness of non-trivial logic.
- andreasvc 12y agoYour argument that typing is tested implicitly or doesn't matter is false, because your function might have 73 code paths, of which your test only covers 1. Tests and types are complementary. A test will prove that in at least one case the function does what it should, but a type system can prove that in all cases at least the types are correct. There was a blog post describing this as establishing a upper vs. a lower bound.
- seanmcdirmid 12y agoC# has local type inference, and is getting a bit better in type parameter inference in the next version. The languages with strong type inference (say OCaml or Haskell) provide less in the form of tooling like autocompletion either through neglect (their communities don't demand it) or design limitations in Hindley Milner (mostly algebraic and structural, poor support for nominal typing where code completion menus begin to make more sense in ways that we currently understand them). Scala would be in the sweet spot of a well tooled statically typed language if the IDE situation could ever be straightened out enough. > For functions with sufficiently rich types, there are often only a couple ways to implement the function. That is only kind of true in pure function land. Any kind of state manipulation, even though monads with powerful dependent typing, drastically opens up how a function can be implemented.
- pcwalton 12y ago> That is only kind of true only pure function land. More than that: it's only true in total pure function land. Admitting divergence explodes the state space of functions.
- dllthomas 12y agoStrictly, yes. However, most of the time this bites less hard than it might, because most of the non-total implementations are obviously wrong where we intend a total function. It still can bite, of course, and I've been meaning to play around in some total languages (or languages with a compiler-enforced total subset).