
Type Wars - ingve
http://blog.cleancoder.com/uncle-bob/2016/05/01/TypeWars.html
======
knucklesandwich
Honestly, I feel like the current is going the opposite direction here. In the
mid-to-late 2000s the most ascendent languages seemed to be dynamic ones.
Ruby, Python, PHP, Javascript, etc. Perhaps that's web solipsism, but those
are the languages I remember having the highest visibility from that time.

Currently, the tide seems to be changing to towards static typing (and
stronger typing). Swift, rust, go, scala, etc. Already statically typed
languages seem to be borrowing ideas from their stronger typed brethren. C++
gaining concepts (similar to typeclasses from haskell), Java encouraging the
use of Optional (similar to Maybe sum types) for nullable values, etc. Many
dynamic languages seem to be providing type checking as an option. Clojure,
python with PEP 484, various javascript dialects and type system addons, etc.

Tests are important and should supplement a static type system, since there
are many classes of bugs a static type system can't prevent. But the benefits
of static typing as opposed to TDD for verifying the same thing are obvious to
me:

1\. Tests are not proofs. Unless a test is exhaustive on the set of inputs
accepted, it cannot prove that a piece of code works correctly. Types can
prove that certain inputs can never be received by a piece of code, which is a
stronger guarantee.

2\. Tests are more work. Why write tests to verify assertions that a compiler
can prove for me? In practice I find this means that unit tests attempting to
verify correct behavior for incorrect inputs have gaps or are imperfect
because of lack of time or lack of foresight.

3\. Tests are still code. Test assertions can have bugs in the same way that
regular code can have bugs. True enough, a type system is also code and can
also have bugs, but the code in a type system is more visible and more
attended to. In practice, I have found many bugs from flawed test assertions
and none from a broken type system (as far as I know).

~~~
crdoconnor
>Tests are not proofs

Neither is static typing. It's a layer of protection, much like tests.

>Tests are more work

I don't think that's necessarily true. You _still_ need to write tests with or
without static typing. Those tests you need to write anyway will usually catch
type errors in a dynamically typed language that a statically typed language
picks up at compile time.

The question is whether you need to write _more_ tests (probably you
do...maybe 5% more), and whether the greater number of tests offsets the
greater amount of code you need to write to statically type your code.

As far as I'm concerned, that's not an easily answered question.

>Tests are still code. Test assertions can have bugs in the same way that
regular code can have bugs

Ditto for types.

~~~
Silhouette
_> Tests are not proofs_

 _Neither is static typing. It 's a layer of protection, much like tests._

Proofs are _exactly_ what the results of strong static type systems give you.
They can't prove _everything_ , but they do prove certain things in all
possible cases.

~~~
jroesch
If you are willing to adopt a strong enough type system pretty much any
interesting property about a program you want to prove is provable. Its just a
matter of ergonomics, more complex type systems provide power, but require
investment in understanding, both conceptually, and in modeling your problem.

You can also adopt automated verification techniques that can prove strong
properties about a system automatically.

~~~
Silhouette
All true, up to a point at least. Strong, static type systems aren't universal
wins with no drawbacks.

However, I think it's fair to say that even the "strongish, staticish" type
systems in a lot of mainstream languages can still prove very useful
properties that are often sources of bugs in the more dynamic languages. A
good example would be not accidentally allowing null values to be passed
around, as mentioned elsewhere in this discussion. And of course some of less
well known but still not uncommon languages, such as the popular functional
programming choices or newer offerings like Rust, can do quite a lot more
without their type systems becoming an unreasonable burden.

------
m_fayer
>The pendulum is quickly swinging towards dynamic typing. Programmers are
leaving the statically typed languages like C++, Java, and C# in favor of the
dynamically typed languages like Ruby and Python.

Even though this article is recent, it reads like it's a good several years
out of date. From where I sit, Ruby and Python are incumbents and the trend is
moving toward typed languages of several varieties. From ML variants to Scala
to the "streamlined functional-ish" languages like Swift, Kotlin, whatever C#
is morphing into, to Typescript, and so on.

He presents TDD as a convenient way to escape the shackles of static-typing. I
think many, myself included, would regard compiler-enforced type safety as a
way to leverage a machine to ease the significant burden of writing and
maintaining a huge test suite. Not to mention the productivity boost provided
by the comparatively richer tooling that static typing enables.

------
giaour
> Why am I wasting time satisfying the type constraints of Java when my unit
> tests are already checking everything?

Couldn't you invert the question for Java and instead ask, why am I wasting my
time chasing 100% test coverage when the type constraints of the language
guarantee a certain degree of correctness?

I've always found that dynamic language projects require twice as many tests
as a Java project to get a similarly reliable test suite.

~~~
mike_hock
Also, how does full _unit_ (!) test coverage prevent a situation where unit A
passes some type to unit B that unit B doesn't expect but happily runs with,
but with _different semantics?_

Documentation of interfaces in dynamic languages usually always specify the
types of arguments and return values, and the code very often looks like this:

    
    
        def foo(arg)
          raise TypeError, "Integer argument expected" unless arg.kind_of?(Integer)
          # ...
        end
    

Yup. That saved me a lot of typing (no pun intended) compared to

    
    
        void foo(int arg) {
           // ...
        }
    

Reading (and writing) code in dynamic languages (including the tests!) always
seems to me to disprove all of their purported benefits and underlines the
need for a strong type system.

~~~
spion
Yup, the right tool for the job. Type systems are really good at enforcing
inter-unit contracts, while tests (especially property tests) are very good at
making sure that the implementation is correct for at least a subset of
inputs.

------
Silhouette
Unfortunately, this post doesn't seem to improve on Martin's usual fundamental
misunderstandings. He still doesn't seem to understand that unit tests verify
single cases but strong type systems prove general cases, and that in
situations that could be handled with either approach, the latter is strictly
more powerful. He still seems to believe that dynamic languages are the way
things are going, despite almost every successful large and complex software
system still being written in the popular static languages he dismisses. He
still thinks dynamic languages like Python and Ruby are great for
productivity, but he's primarily comparing them to Java as the standard for
productivity with statically type languages.

This is the man who, somewhere around 2011 I think, claimed that we might have
explored the whole programming language space, and there might not be any new
programming languages left to be invented. He's also the man who, going by the
very post we're discussing, seems to think that having typing so restrictive
that _not everything is nullable by default_ is some sort of radical new idea.
(Compare that with Tony Hoare's conference speech in 2009, in which he called
inventing the null reference his billion dollar mistake, and notice that
almost every modern static language provides this kind of safeguard.) So I'm
not sure we should take Robert Martin's predictions for where the programming
industry is going too seriously; indeed, he should perhaps learn a bit more
about what is already available today before making big public predictions
about tomorrow.

------
wyager
> The language is very opinionated about type safety... For example, the fact
> that a variable of type X might also be nil means you must declare that
> variable to hold an "optional" value... The extreme nature of the type
> system in swift

Oh boy, if he thinks that's extreme...

> Why am I wasting time satisfying the type constraints of Java when my unit
> tests are already checking everything?

Oh really? Are the unit tests testing every single 2^64 possible values of an
Int? Are they testing for the existence of null everywhere you have a pointer?
Strong types aren't just a little bit more info about something; they're a
precise refinement of the nature of all values in a program.

While very few type systems can test _everything_ you can test with unit
tests, a decent type system and good semantics can cover all causes of crash
failures and many causes of logic errors. Seriously, folks; writing programs
that crash is more or less optional these days. Writing correct programs is
still hard, but you can make it a lot easier on yourself.

~~~
mpweiher
> Are the unit tests testing every single 2^64 possible values of an Int?

Hmm...your usual type checker is checking 0 possible values of an int. It is
checking just the type. And of course if you are checking a value in a test,
you ar coincidentally also checking its type.

That's not to say that types can't be useful (for example they have been shown
to be quite useful as machine-checked documentation for getting around
unfamiliar codebases), but please let's not oversell...

~~~
wyager
In fact, it checks every predicate codified by the type system. The question
is how powerful your type system is, because this determines what predicates
you can (conveniently) test.

Well-designed strongly typed languages naturally check some useful predicates
(existence, the exact structure of the value, etc.). These alone are
sufficient to eliminate null pointers, non-total functions, and most other
failure modes that plague languages like C++ or Python.

Stepping it up a notch, you can use things like GADTs and kind promotion to
check much more advanced predicates (like that a vector must be non-empty or
even length).

With refinement types a la liquid Haskell, I can statically guarantee that
e.g. my function only returns lists of even length or only returns even
numbers, without even having to put this information in the type of the
returned value.

With full-blown dependent types a la Coq/Idris/Agda, you can encode pretty
much whatever property you want into types. For example, Idris has
`verifiedMonoid` which statically guarantees that an operation is associative
and has an identity. See [https://github.com/idris-lang/Idris-
dev/blob/master/libs/con...](https://github.com/idris-lang/Idris-
dev/blob/master/libs/contrib/Classes/Verified.idr)

------
shasta
"On the eve of publication, Bertrand Russel wrote to Frege and pointed out
that Frege's logical system allowed statements that were ambiguous -- neither
false nor true."

This makes it sound as though Russel showed the system to be incomplete, and
the reference to Godel's theorem in the next paragraph reinforces the
confusion. Russel instead showed the system to be inconsistent, which is much
worse.

------
efnx
It's disappointing to see this post makes no mention of any ML style
functional language or even Rust. It's hard to take a post with this kind of
title (and predictions) seriously when it doesn't even mention the big
(relatively) new players in the "Type Wars".

------
nercury
I don't know about what the author's goals are, but I am in favor of
offloading as much work to computer as possible.

To err is to be human, but compilers never forget.

~~~
josephmoniz
Good point, the author clearly misses the point that tests themselves are code
and thus can have errors in them as well. Code coverage is just a vanity
metric if you're tests don't actually test the correct thing.

~~~
mpweiher
Types are also code and can have errors in them...

~~~
wvenable
Types aren't code.

------
echelon
Without those pesky types, how does one do automated refactoring in a large
codebase? By hand?

Types are one of the hallmarks of large scale engineering. Any codebase of
appreciable size without types is difficult to work in, at least in my own
experience.

It takes a little bit more effort writing the types, but doing so saves so
much time in the long run that it's absolutely worth it.

~~~
mpweiher
> Without those pesky types, how does one

> do automated refactoring in a large codebase?

> By hand?

Dunno. One could use the RefactoringBrowser. The first automated refactoring
tool. Ever. In Smalltalk. A dynamically typed programming language.

The authors even talked about the tradeoffs of using a dynamic language. They
thought the problems would be bigger than they turned out to be.

[http://dl.acm.org/citation.cfm?id=280610](http://dl.acm.org/citation.cfm?id=280610)

[http://www.refactory.com/tools/refactoring-
browser](http://www.refactory.com/tools/refactoring-browser)

[http://c2.com/cgi/wiki?RefactoringBrowser](http://c2.com/cgi/wiki?RefactoringBrowser)

------
joslin01
> My own prediction is that TDD is the deciding factor.

If this turns out to be the case, then people would reach for statically typed
languages to augment & cut-down on tests.

Like everyone here is saying, the pendulum is moving toward statically typed.
The next generation will probably be in higher kinded types as we continue to
attempt to write more generic code that cuts down on number of lines, but
still preserves type guarantees.

Finally, I don't think it's appropriate to regard `Option[T]` as some mystical
type. It's just a regular old generic class that wraps something. Scala
programmers (and I'm sure others) have been doing this with `Either[A,B]` &
`Option[A]` for awhile, but Swift was really clever in building it into the
language with the `?` & `!` operators.

~~~
paulddraper
"Clever" or "overly specialized", depending on your viewpoint.

~~~
jroesch
I think they made a much better decision then everyone else even if verbose.

Nearly every language has continued to repeat Tony Hoare's "billion dollar"
mistake of allowing null to inhabit any type. I think approaches like Rust's
and Swift's are steps to stamp out something that never should of existed in
the first place.

~~~
paulddraper
> Nearly every language has continued to repeat Tony Hoare's "billion dollar"
> mistake of allowing null to inhabit any type.

Perhaps I was unclear. I could not agree more with this assessment (see link
to my blog post in sibling).

I was merely commenting on Swift's choice to do it in a (to quote the OP)
"clever" way, rather than a general one like Rust or Scala.

------
agazso
Modern type systems made type inference and gradual typing possible and those
are making a huge difference nowadays.

With type inference you can write programs almost as easily and productively
as in dynamic languages. With gradual typing you can basically write the same
code as in a dynamic language and still get type safety (e.g. TypeScript).

Swift, Go, (Rust, Scala, C# etc. but even C++) has type inference today. Maybe
dynamic languages will get type annotations and gradual typing in the future
and then basically we can have the best of both worlds.

------
paulddraper
There are many concepts which approach equivalence, e.g. the IO monad and the
C preprocessor.

[http://conal.net/blog/posts/the-c-language-is-purely-
functio...](http://conal.net/blog/posts/the-c-language-is-purely-functional)

Likewise, tests and type systems approach equivalence (especially with
dependent types).

Thus the distinctions are practical rather than purely theoretical.

(1) In practice, I find the static typing approach to result in more complete
"tests".

(2) In practice, I don't see people writing reusable tests when they share
code with others. However static types are are shared with the user's code,
thus reducing the testing they need to write themselves.

------
josephmoniz
>The pendulum is quickly swinging towards dynamic typing. Programmers are
leaving the statically typed languages like C++, Java, and C# in favor of the
dynamically typed languages like Ruby and Python. And yet, the new languages
that are appearing, languages like go and swift appear to be reasserting
static typing?

The author makes the claim that the pendulum is swinging back towards dynamic
typing but then follows up immediately with the contradictory evidence that
the new hip languages showing up are strongly typed.

I'm curious what if any evidence can be presented that this pendulum is
swinging in any direction as oppose to just pulling at opposite ends as it has
been for quite some time.

------
jsmith0295
While it might seem like things are shifting back towards static typing, I
think that is primarily happening in the development of technology now but not
the use of that technology yet. I think these predictions will probably appear
to be true for some period of time before these newer typed languages begin to
gain larger amounts of usage. (However, I expect the testing will probably
stick around. I'm highly skeptical of the idea that typing and testing are
some how interchangeable.)

Also, I think it'll be interesting to see what role gradual typing systems
like Flow, Hack, and TypeScript might have on getting static typing into
existing dynamically typed codebases.

------
tunesmith
Ruby salaries tend to be higher than JVM salaries? What?

~~~
andy_boot
I was surprised by that statement too, I don't think it is true in London.

~~~
matthewmacleod
Really? My experience is that while there are a number of high-end Java
positions, and a number of Ruby positions in well-funded startups and such,
there are VAST numbers of Java positions for mediocre engineers and mediocre
companies – no fault of the language, more of the niche it has found itself
in. I'm not surprised at all to find that this drags the average salary down.

------
alkonaut
> "Why am I wasting time satisfying the type constraints of Java when my unit
> tests are already checking everything"

Not only is this a straw man, it's also a really weak argument.

Types _prove_ the _absence_ of (type) bugs. Tests can merely prove the
presence of such bugs.

