5 ms·
To some extent, "let it break" is not all that different from "let the type system guide what needs to be fixed." The difference is in what's breaking. The type
by nilkn 10y ago
To some extent, "let it break" is not all that different from "let the type system guide what needs to be fixed." The difference is in what's breaking. The type-driven approach is basically "let the type coherence of the program break." In other words, compile the program, knowing fully well that it won't successfully compile. You see exactly where the types break down, and you can then figure out what needs to be changed.
With all that said, I like your distinction at the beginning of your comment a lot. A lot of my recent work has focused very much on a problem domain where the input data is very well understood, but the transformations applied to that data are complex and not all that well understood. I've found that Haskell has worked extremely well for me in this case, but I think you make a compelling point that it may not work quite so well in cases where the input data is the unknown, but the transformations are fairly well understood already. Perhaps this is why languages like Python have dominated the machine learning space, and strictly-typed languages just never really took off there.
- nostrademons 10y agoThe advantage of "let it break" is that you can continue to run the portion of the program that remains coherent. It's letting the user (who is often just yourself, or a small group of beta testers) into the system; if the user continues to find value in the program even when it's broken, the part that's broken must not have been that important. It's not unusual for programmers to overspecify the requirements when the requirements are unknown; you have to make a number of (often arbitrary) decisions just to get a program working end-to-end, and there's no guarantee that those decisions align with how a user will get the most value from the system.
- coolsunglasses 10y ago>The advantage of "let it break" is that you can continue to run the portion of the program that remains coherent. This works fine in Haskell, thanks in part to laziness. There needn't be a tradeoff here.
- nostrademons 10y agoEven if it fails to typecheck? Has the Haskell compiler gotten smart enough that it can stub out portions of the code with type errors and fail at runtime?
- pinkythepig 10y agoActually yes, you can pass a compiler flag, -fdefer-type-errors, which makes type errors turn from compilation errors to simply warnings. That said, any code that does not type check will also not run and instead will throw an exception at runtime if called (laziness is key here). But if Part B was giving type errors and you only cared about testing Part A, then you are good to go.
- sordina 10y agoYes. You can defer type errors. I turn this on in development.
- coolsunglasses 10y agoYep. It's easier to implement and makes more sense in a lazy language than what you're describing though. I don't use it in my work but I do use a related technique to isolate what I am focused on fixing.
- coolsunglasses 10y agoTo elaborate a little on this: In a strict language, a type error would mean the bricked off expression that doesn't type-check would spread like an infection to any expression that incorporated it. In Haskell, I can dance around sub-expressions that don't type-check at runtime and evaluate things optionally. The technique doesn't work in practice otherwise.
- tome 10y ago> The technique doesn't work in practice otherwise. I don't see why. Functions in a strict language are evaluated lazily. Any type error within a function wouldn't spread beyond that function.
- nilkn 10y agoAnd the disadvantage of that is that you possibly don't even know what's broken! It will come down entirely to how vigilant you've been with testing. Your argument is that if it's broken but you don't notice, then it must not be all that important. But I think there are tons of exceptions to that, and that's putting a huge amount of faith in yourself and beta testers to reliably test all important aspects of the software. Besides, if you do more or less figure out what's broken and what's not important, you still have to engage in a flimsy surgery of the codebase to excise the broken pieces. I'm not arguing universally against what you're proposing by any means, but it doesn't seem like a clearly superior approach to me. The bottom line is that "let it break" takes many forms. There's your version, there's my type coherence version, there's Erlang's version with supervisor trees, etc. I think it's hard to argue that any of these is universally superior to the others.
- nostrademons 10y agoIt's superior for a certain problem domain, one in which the desired feature set is unknown, the desired feature set is simple, and the consequences of failure are limited. Consumer web & mobile startups, basically, as well as some limited small-business B2B software where the alternative is an error-prone manual process. It's probably not appropriate for enterprise software, and is certainly not appropriate for embedded, regulated, or safety-critical software. This is far from the whole software industry, but it's a segment that tends to be of disproportionate interest to HN readers, given the startup-focused origins of this site.