7 ms·
> The trap is that both OOP hierarchies and FP "make illegal states unrepresentable" create premature crystallization of domain understanding into rigid technic
by seanwilson 1y ago
> The trap is that both OOP hierarchies and FP "make illegal states unrepresentable" create premature crystallization of domain understanding into rigid technical models. When domains evolve (and they always do), this coupling demands expensive refactoring.
At least when you refactor your types, the compiler is going to pinpoint every line of code where you now have missing pattern checks, unhandled nulls, not enough parameters, type mismatches etc.
I find refactoring in languages like Python/JavaScript/PHP terrifying because of the lack of this and it makes me much less likely to refactor.
Even with a test suite (which you should have even when using types), it's not going to exhaustively catch problems the type system could catch (maybe you can trudge through several null errors your tests triggered but there could be many more lurking), working backwards to figure out what caused each runtime test error is ad-hoc and draining (like tracing back where a variable value came and why it was unexpectedly null), and having to write + refactor extra tests to make up for the lack of types is a maintenance burden.
Also, most test suites I see do not contain type related tests like sending the wrong types to function parameters because it's so tedious and verbose to do this for every function and parameter, which is a massive test coverage hole. This is especially true for nested data structures that contain a mixture of types, arrays, and optional fields.
I feel like I'm never going to understand how some people are happy with a test suite and figuring out runtime errors over a magic tool that says "without even running any parameters through this function, this line can have an unhandled null error you should fix". How could you not want that and the peace of mind that comes with it?
- 9rx 1y ago> it's not going to exhaustively catch problems the type system could catch Unless you are using a formal proof language, you're going to have that problem anyway. It's always humorous when you read comments like these and you find out they are using Rust or something similar with a half-assed type system.
- paulddraper 1y agoThere’s a difference between “there’s some gaps” and “you can drive a bus through it.” Lots of languages other than Rust have static types, some more complete than others.
- 9rx 1y agoTrouble is that if there are gaps then the types become redundant. Consider error handling. Rust helps ensure you handle the error, but it doesn't ensure you handle the error correctly. For that, you must write tests. But once you've written tests to prove that you've handled the error correctly, you've also proven that you handled the error, so you didn't really need the type to begin with. You're really no better off than someone using PHP or JavaScript.
- bccdee 1y ago> But once you've written tests to prove that you've handled the error correctly, you've also proven that you handled the error Hardly. Suppose all caught errors in a particular module of code bubble up to a call site which (say) retries with exponential back-off. If the compiler can guarantee that I handle every error, I only need one test that checks whether the exponential back-off logic works. With no error handling guarantee, I'd need to test that every error case is correctly caught—otherwise my output might be corrupted.
- jcranmer 1y agoThe chief reason software fails is because programmers are insufficiently aware of all of the reasons the software can fail. Sure you need a test to make sure that you handle the error correctly, but if the function signature doesn't indicate the possibility of an error occurring, why would you write that test in the first place?
- 9rx 1y agoBy the same reasoning, if the function signature doesn't indicate what specific errors can occur, why would you write a test in the first place? No matter how you slice it you have to figure out what the software you are calling upon does and how it is intended to function. Which is, too, why you are writing tests: So that your users have documentation to learn that information from. That is what tests are for. That is what testing is all about! That it is also executable is merely to prove that what is documented is true.
- seanwilson 1y agoI caveated you should have a test suite anyway (i.e. because types aren't going to catch everything), and the above was suppose to be a caveat to mean "for the behaviours the type system you have available can catch". Obviously mainstream statically typed languages can't formally verify all complex app behaviour. My frustration is more aimed at having time and energy wasted from runtime and test suite errors that can be easily caught with a basic type system with minimal effort e.g. null checks, function parameters are correct type. Formal proof languages are a long way from being practical for regular apps, and require massive effort for diminishing returns, so we have to be practical to plug some of this gap with test cases and good enough type systems.
- 9rx 1y ago> e.g. null checks, function parameters are correct type. Once you've tested the complex things that (almost) no language has a type system able to express, you also have tested null checks, function parameter types, etc. by virtue of you needing to visit those situations in order to test the complex logic. This isn't a real problem. What you might be trying to suggest, though, is that half-assed type systems are easier to understand for average developers, so they are more likely to use them correctly and thus feel the benefit from that? It is true that in order to write good tests you need to share a formal proof-esq mindset, and thus they are nearly as burdensome to write as using a formal proof language. In practice, a lot of developers don't grasp that and end up writing tests that serve no purpose. That is a good point.
- seanwilson 1y ago> Once you've tested the complex things that (almost) no language has a type system able to express, you also have tested null checks, function parameter types, etc. by virtue of you needing to visit those situations in order to test the complex logic. This isn't a real problem. I just don't find this in practice. For example, I've worked in multiple large Python projects with lots of test cases, and nobody is making the effort to check what happens when you pass incorrect types, badly formed input, and null values in different permutations to each function because it's too much effort and tedious. Most tests are happy path tests, a few error handling tests if you're lucky, for a few example values that are going to miss a lot of edges. And let's be honest, it's common for parts of the code to have no tests at all because the deadline was too tight or it's deemed not important. If you have a type system that lets you capture properties like "this parameter should not be null", why would you not leverage this? It's so easily in the sweet spot for me of minimal effort, high reward e.g. eliminates null errors, makes refactoring easier later, that I don't want to use languages that expect me to write test cases for this. > half-assed type systems are easier to understand for average developers Not sure why you call them that. Language designers are always trying find a sweet spot with their type systems, in terms of how hard it is to use and what payback you get. For example, once you try to capture even basic properties about e.g. the size/length of collections in the types, the burden on the dev gets unreasonable high very quickly (like requiring devs to write proofs). It's a choice to make them less powerful.
- deleted 1y ago[deleted]
- benrutter 1y agoYou're right, but kind of missing the way risk works. Normally you want something like a swiss cheese model[0] where different layers reduce the likelihood of issues. Snubbing type systems because they aren't 100% failproof misses that point. [0] https://en.m.wikipedia.org/wiki/Swiss_cheese_model https://en.m.wikipedia.org/wiki/Swiss_cheese_model