[Haskell-cafe] property testing with context
ben.franksen at online.de
Wed Oct 24 07:44:48 UTC 2018
Am 19.10.2018 um 22:53 schrieb Olaf Klinke:
> the solution might be simple: Don't express the property as an atomic
> function (a -> Bool). This reminds me a bit of the Cutty-Howard
> correspondence, only that proofs are replaced by counterexamples. A
> testing library should have an operator for each of the logical
> connectives, where
> - a counterexample to True is never found
> - a counterexample to False is ()
> - a counterexample to (not x) is an example for x
> - a counterexample to (x && y) is either a counterexample to x or a counterexample to y, whichever is found first
> - a counterexample to (x || y) is a pair of counterexamples to both x and y
> - a counterexample to (x ==> y) is an example for x together with a counterexample for y
> Then, if you build your property out of smaller properties and the connectives above, the counterexample-finder would be able to inform you which part of the property failed. I'm pretty sure at least one of the testing libraries has a system like this.
while this approach works in principle it has practical disadvantages.
One charming feature of property testing in Haskell is that expressing
properties does normally /not/ require to import the testing library.
This allows me to write my properties in a generic style that works with
e.g. quickcheck, leancheck, smallcheck etc. With your approach I would
be tied to a single property testing library.
I guess this would be less of a problem if in Haskell the boolean
operators were overloaded i.e. part of a class. I think this was an
oversight when the Prelude and the standard libraries were designed. IME
boolean algebras (or perhaps more generally lattices) appear
/everywhere/. I can't count the number of times I wanted to use these
operators on structures containing Bools or functions returning Bool and
couldn't. IMHO such a generalization would be a lot more useful and less
controversial than the one from List to Foldable.
(I can't remember a single instance where I missed this generalization
of lists. In fact I think is completely useless. Foldable essentially
allows me to say: "give me all your elements, one by one, in /your/
preferred order, so I can combine them to something new". Due to the
laziness of lists this is really the same as having a toList function
(modulo list fusion to take care of the constant factor overhead). Only
that a container type may offer many different versions of toList but
only a single Foldable instance. I understand that foldr and foldl have
been added to the class to allow at least two different "preferred"
traversal orders, but adding methods to a class in this way obviously
doesn't scale and is poor design, again IMHO.)
More information about the Haskell-Cafe