Hacker News new | ask | show | jobs
by rdtsc 3234 days ago
Good post, but note all those things you don't need a statically typed language. Erlang for example is a dynamically typed language but it has Dialyzer - a type checker. The more precisely you define the types the more discrepancies and issues it will find in the code, exactly the kind of stuff the author mentions.

http://learnyousomeerlang.com/dialyzer

That's technically called "success typing" http://www.it.uu.se/research/group/hipe/papers/succ_types.pd...

Python has that too with MyPy. That came much later than Erlang and I haven't used yet so not sure how well it works

http://mypy-lang.org/

They seemed to have copied success typing but don't actually mention it anywhere.

2 comments

> Good post, but note all those things you don't need a statically typed language.

> Python has that too with MyPy.

I don't know much about Erlang's typing, but this claim is definitely untrue. I run a code base with five engineers and I'm pretty disciplined about asking for type annotations wherever appropriate, and I still miss static typing every damn day. The grafted-on approach is definitely beneficial, and I frankly don't understand how people ran Python engineering projects of any significant size without it, but it has just enough holes in it that the type inference chain breaks often (ie just drops to type Any). It also gives false positive errors just often enough that it lowers your sensitivity to real errors.

Don't get me wrong, I'm happy it exists. But Python with types is a poor imitation of a proper engineering programming language.

I've worked in an modest Erlang codebase (~30k LOC) with a couple other people, and we used dialyzer pervasively with pretty good success. I would actually say that in some ways it was an improvement over Java's approach to types:

* there is no implicit 'null'. I.e., if your type signature doesn't explicitly include the value "none", then it's not allowed

* you have union and intersection types

* it's super easy to create new records (and types from those records)

* even if your type signature is structural, you can optionally give more meaningful names to arguments and return values in the signature itself

* success typing is still good enough to catch many "uninteresting" bugs (which I consider to be a significant advantage of static typing. When I fix bugs, I want it to be bugs with my understanding of the requirements, not bugs with my understanding about the values returned/expected by a function)

pattern matching also helps clarify the expected values of a function.

> and I frankly don't understand how people ran Python engineering projects of any significant size without it

Pretty much the first thing the Zope folks made was port Java interfaces to Python.

Considering that Zope projects were both relatively early and big in Python's life time, that might answer your question. (Plus, projects not using anything like it usually either have very large test suites, or are broken more frequently than working).

Erlang with dialyzer is a great "mix", it allows you to move as quickly you would with a dynamic language and, as long as you define the type specs, have a reassuring type safety. Of course it's nowhere near haskell's or elms type safety because, as you said, dialyzer follows the success typing model, sort of like an optimistic one ("you are correct until I prove you otherwise"), and haskell and elm follow the opposite way ("you are wrong until you prove me otherwise ").

It's a tradeoff, as everything else in computer science, and one that has worked for us. Can't recommend both erlang and dialyzer enough :)

The funny thing about "moving quickly in dynamic languages" is that it doesn't last. Refactoring code in a dynamic language gets difficult fast and is an entirely hopeless endeavour without a huge test suite (you obviously need tests on every level, because you can't refactor an interface and its test at the same time and still think it's working as intended).

Typed code may be slower and more cumbersome (to some) to write in the first place, but is usually much easier to maintain in my experience.

I find that anything beyond a very small program is faster to do with sort of manifest typing (with static analysis/type checking).

Now, I think that type inference for local variables can be nice (especially if you have good IDE that allows you to see the inferred type).

I once wrote a 600-700 line application in PowerShell. I found myself adding a few type annotations, and I expect that a program any bigger than that (especially if more than one person started working on it) would benefit from a policy of always adding type annotations.

Success typing really seems both ideal for me and the only sane way to do optional, progressive typing. I wonder if Ruby could do it? I think there are too many ways for an expression to be invoked from friggin anywhere in Ruby to make that possible.