12 comments

[ 3.2 ms ] story [ 37.5 ms ] thread
Isn't the bogus code trivially caught by linear type languages like Austral? Maybe we need to move past urBEAM and into futureBEAM
Yes. Also by the typestate pattern: have open and closed files have different types, and write be implemented only for open files
Nothing short of linear types prevent you from saving the open file descriptor to a variable, closing the file, then using the open file descriptor variable again.
That's true! It's not an "also", you use both linear/affine types plus the typestate pattern

For the curious, it works like this in Rust http://cliffle.com/blog/rust-typestate/ https://willcrichton.net/rust-api-type-patterns/typestate.ht...

Or rather, typestate itself needs either linear/affine types or a similar mechanism to make sure that when you are in the "closed" state you can't access methods from the "open" state, and vice-versa. (now, there are languages that implement typestate as a language feature rather than a design pattern, but I don't know how they work)

How exactly "Let it crash" solves whole class of bugs in code, that cause the application return wrong result but complete without any errors? That is the stuff I will be writing unit/integration tests, no matter the language/platform.
Let it crash does't solve these problems. Let it crash solves random disconnections, machines dying and being replaced by others, unexpected network partitions and processes being killed because OOM.

I would be surprised if you write unit tests for these cases.

> But with all that, as Joe Armstrong once jeered, no amount of type checking would catch the following bogus code:

It is worth to note that the stated problem, of accessing a file after it is closed was addressed in the Phd thesis by [1] Joe Armstrong in which he suggested a solution for exactly this problem by introducing a new testing & development methodology he named "protocol checkers" (9.1 Protocols, page 195). Relevant quote from the thesis:

> Given a protocol which is specified in a manner similar to the above it is possible to write a simple “protocol checking” program which can be placed between any pair of processes.

The protocol is a state machine, which would detect the attempt to write to a file after it was closed.

[1] - https://erlang.org/download/armstrong_thesis_2003.pdf

I have a rather large amount of respect and admiration for Joe Armstrong and his accomplishments, but I think he got this somewhat wrong.

Linear types could potentially catch that bug. In fact I think Linear type system can help us go from "ints are file descriptors" to "what kind of socket is this and can I call this function on it?" to "what's the state of this FD?".

I agree that if we treat FD's as "int", you're hosed in this case, but that's the world we live in, and I think that's what he was getting at.

the saving grace for dynamic languages are unit / integration tests. how else do you do a successful refactor without breaking production?
This post seems kind of incoherent. I think the premise is that, if you use the OTP properly, then you are generally protected from bugs by the properties of the OTP, and therefore you rarely (if ever) need to rely on testing or static type checking to catch those bugs. But I don't think the author is aware of the different value propositions of these tools - they each solve different problems, and work best in conjunction rather than separately.

With tests, for example, the big benefit is not checking whether code runs or not, it's checking whether the code does what you expect. I've been writing a surprising amount of code recently involving indices and slices of arrays, and it's often very easy to get that sort of code subtly wrong: including an element that shouldn't be there, missing one out, or surprising things happening when the list is empty. Types can't solve this problem, and it's unlikely that anything will crash per se, but you don't want to release to production and suddenly find duplicate data floating around the system because of some bad pagination code. Enter tests: I can write down a list of all the cases that are likely to be interesting, put together a test case for each one, and then make sure I've not forgotten anything in my implementation. Moreover, anyone who makes future changes can't accidentally break that logic, because the tests will keep on running. That's something tests can do that types and the magic powers of the OTP can't touch.

On the other hand, just today I was throwing together a quick reproduction script for a bug in a Python project. Because the aim was to find a minimal reproduction, there was a lot of refactoring going on - switching out libraries, adding and removing asyncio support, deleting functions, etc. Quite often, I'd make a change, and then need to make changes in other parts of the script to accommodate the changes (swap out a library -> change a couple of function names; add asyncio -> make sure everything is awaited properly, etc). Now I could have written tests for this script, but that would have taken ages and not been very useful. Or I could have run it and let it crash, as is the OTP motto, but even the takes time. But because MyPy is turned on by default in my editor, I got type feedback immediately on my code, which meant I could fix all the problems just by looking for the red squiggles. And sure, I still needed to run the code to make sure I could still reproduce this specific bug, but I didn't need to run the code just to see if it worked. The type checker solved problems for me that would have been slower if I'd been using just tests or a "let it crash" philosophy.

It seems to me that it is very useful to use each tool in the places that it is most valuable: tests to check that your code behaves logically and correctly; types to provide a sanity check and quick feedback on obvious mistakes; and the OTP to ensure strong reliability in the face of errors and bugs that slip through the other two defences. Using all three should surely give you better results than using one alone, especially if you use those tools according to their strengths.