Hacker News new | ask | show | jobs
by Jtsummers 1611 days ago
Pre/post conditions are complementary to a type system. They can ensure logical properties that may not be encodable in your underlying type system (that is, essentially every mainstream statically typed language). Such as the relationship between two values in a collection. Trivial example, if you have a range such as [x,y] where x < y must hold, how would you convey that in any mainstream type system?
1 comments

The Haskell-y way to do this is to use a smart constructor[0].

[0]: https://wiki.haskell.org/Smart_constructors

The first part of that page demonstrates what amounts to pre/post conditions, but placed in the constructor. The range is checked dynamically, not statically.

The second part is using Peano numbers to enforce the constraint. I guess you could try and force that into some mainstream languages, probably C++. With its template programming you could get something going in this vein, though I'm not sure how well it would work if the number were calculated at runtime rather than compile time. You'd still end up with a dynamic check somewhere.

The way that the value floats through the system is checked statically, and the program can (and should) be designed so that the value with the appropriate type cannot be constructed unsafely.

If you need to statically check the construction of values in Haskell, there are things like refinement types[0].

[0]: http://nikita-volkov.github.io/refined/

> The way that the value floats through the system is checked statically, and the program can (and should) be designed so that the value with the appropriate type cannot be constructed unsafely.

Except that in the first example from the first link you sent me, there is no static guarantee that the inputs to the constructor are valid, thus the error branch (it would be unnecessary if static guarantees could be made regarding the use of the constructor). And that was my point, that you still end up with dynamic checks on the values which is where pre/post conditions step in to cover what static typing cannot (or, again, cannot easily in mainstream languages, which would not be Haskell).

Is there any reason you didn't address the second link that I shared?
Mostly because, per my reading (as an admitted Haskell dabbler and not fluent), it looks like a variation on a theme rather than a totally different thing. It seems to be a more consistent and refined (har har) way of doing the same thing as the first link, and still has a dynamic check (at least in one form, refine vs refineTH) just like the smart constructors.

But also because we got derailed from my initial point and context.

> Pre/post conditions are complementary to a type system.

Do you disagree or agree with this statement? Because you never addressed it either.

> They can ensure logical properties that may not be encodable in your underlying type system

Note the "may", because that's important. I didn't say that there were no languages in which my example could be encoded in the type system. And maybe it wasn't the best example, but the point itself was that there is no type system (that I'm aware of, not even Idris as far as I know) which can prove in its static type system every piece of logic about a program. This means that some properties of the system will end up being checked (if you bother to) at runtime and not at compile time. That's where pre/post conditions are useful, they contain information (and in a more deliberate form in cases like the Clojure example) about the properties of the system that are hard or impossible to encode directly in the type system.

Different type systems (both static and dynamic) let you express more or less with them, which reduces the need/desire to have checks like these in your program. But I seriously doubt that any mainstream language will ever totally remove their utility, as complements to the rest of the type system.