3 ms·
Maybe I'm misunderstanding, but it looks like every time these functions get called, they're type checking their inputs and outputs? That seems like a waste of
by thinkpad20 13y ago
Maybe I'm misunderstanding, but it looks like every time these functions get called, they're type checking their inputs and outputs? That seems like a waste of resources. Why not just write unit tests that ensure that they will always be called with the types you expect (or fail at an earlier step if not)? Identify the places where there might be ambiguity in something's type (e.g. when a JSON blob arrives, it might map a key to a list instead of a dictionary, or if a function might return multiple types, etc), and use unit testing to ensure that the type you expect will emerge from those places, or else a failure will occur.
- Tobani 13y agoI believe :pre and :post conditions only get run when assertions are enabled. There are other languages that have this feature (Eiffel touts it pretty heavily), but I haven't seen the AHA! example where this is the long lost feature I've been missing. But that said, :pre/:post w/ unit tests seems pretty powerful. You can assign the invariants to the functions themselves and have a better / more robust set of assertions in your unit tests.
- thinkpad20 13y agoIf it only will get run in some sort of testing mode, that's fine I suppose. Although the other drawback to this sort of thing is that it seems to really decrease readability. Most of the time when you're reading code, you just want to see what's actually happening, and having a big messy set of type assertions would be noise 90% of the time (especially because unlike type signatures in Haskell, say, there's no syntactical difference that would allow syntax highlighters to help you visually see what's actual code and what's type assertions).
- Tobani 13y agoSo I wouldn't confuse invariants w/ type assertions. Clojure does have separate type assert/checking in core.typed. Yes one thing you might use :pre/:post for is type checking, but it can do more than just that.
- thinkpad20 13y agoRight, but unit tests do that too. And that would still be something I would in most cases rather handle in unit tests than in assertions in the code itself, for mostly the same reasons.
- rapala 13y agoHow do you write a unit test that tries to assert that no one calls the function sqrt with a negative integer? Because that is precisely the case where pre-conditions can help.
- thinkpad20 13y agoYou don't write unit tests to assert that a function never gets called a certain way. You can't, in a dynamic language: a function could conceivably be called with anything, and even in a statically typed language, there are some functions which cannot be determined at compile-time never to terminate without error. (For example, if you're using signed integers, the input could be negative). The goal of unit testing in this case is to ensure assert that if that happens, your code does what you expect it to, whether that's fail with a nice error message, return some default value, or simply bomb out. Depending on what context that function occurs in, it might be guaranteed to never happen (e.g. sqrt is only ever called by function foo, and foo always calls abs on its input before calling sqrt.). In those cases, it's not necessary to write those assertions into sqrt. You should separate what's actually subject to variability at runtime vs what can be ensured by program flow, and write unit tests accordingly. There's nothing wrong with the kind of assertions/preconditions described above, but I remain skeptical that they offer anything fundamentally stronger than a comprehensive suite of unit tests.
- rapala 13y agoI agree that contracts don't offer something stronger than unit test. They offer something different. Of course you can't write the unit test that I asked and that was the point. But it would be kind of pointless too to write the test that checks that an exception is thrown when sqrt is called with a negative argument. It's trivial to see that happens by looking at the code. How sqrt fails is not interesting either, as you are not supposed to recover from that. A pre-condition is a way to specify that. It says: "Don't call me with a negative argument, just don't."
- deleted 13y ago[deleted]
- bad_user 13y agoUnit testing cannot prove the absence of bugs and shit happens in spite of their presence. That's why I still value (good) static type systems. But besides a good static type system that can prove certain properties about the code (and I'm not talking about Java's type system here), you can also do design by contract [1], which is precisely what @lkrubner is doing and I find that to be pretty cool. Well, it would be cooler if the compiler could check those contracts at compile-time and issue some helpful warnings, but at runtime they are still valuable because the code will fail sooner rather than later. Plus you can probably disable them completely, should you experience problems with performance in production. They also serve as documentation for other developers, documentation that you're forced to keep in sync. This documentation is not about the actual business logic, that ends up being laid out in tests, but rather about interface specifications and invariants. So there you have it - testing serves a different purpose. [1] https://en.wikipedia.org/wiki/Design_by_contract https://en.wikipedia.org/wiki/Design_by_contract
- thinkpad20 13y agoNothing about what I was saying precludes the use of design-by-contract; in fact, that's what I was advocating. You say that "at runtime [type assertions] are still valuable because the code will fail sooner rather than later," which is what I meant by identifying places where there is a possibility for the wrong type to be used, and putting your assertions there. Sticking type assertions on the inputs and outputs of every function you write would be unnecessary, inefficient and horrible to read: def increment(n): assert isinstance(n, int) or isinstance(n, float) result = n + 1 assert isinstance(result, type(n)) return result So as long as we can agree on that much, then we can agree that there is a value to being judicious about where you should and shouldn't use type assertions. By the way I'm a fan of static typing as well, although I see value in dynamic languages too. And I definitely acknowledge the limitations of unit testing, although from a practical point of view, a comprehensive set of unit and functional tests is usually robust enough. And, of course, type systems don't make any guarantees against logic errors. :)
- bad_user 13y ago