Differences to Haskell Hedgehog

Differences to Haskell Hedgehog#

This page documents where the Scala Hedgehog API deviates significantly from the Haskell version.

Result#

The Haskell version allow for assertions throughout the Property monad, but the final value is ().

prop_reverse :: Property
prop_reverse =
property $ do
xs <- forAll $ Gen.list (Range.linear 0 100) Gen.alpha
reverse (reverse xs) === xs

And the corresponding Scala version:

def propReverse: Property =
for {
xs <- Gen.alpha.list(Range.linear(0, 100)).forAll
} yield xs.reverse.reverse ==== xs

Resource Management#

This approach makes it more difficult to isolate resource management in a strict language like Scala. It then becomes fairly important in the Haskell version to use ResourceT:

prop_unix_sort :: Property
prop_unix_sort =
property $ do
values0 <- forAll $ Gen.list (Range.linear 0 100) Gen.alpha
test . runResourceT $ do
dir <- Temp.createTempDirectory Nothing "prop_dir"
...
values0 === values

To simplify this, and to reduce surprises, the final result in the Scala version is now a separate Result value, which forces a single, final assertion to be returned.

def propUnixSort: Property =
for {
values0 <- Gen.alpha.list(Range.linear(0, 100)).forAll
} yield {
val dir = java.io.Files.createTempDirectory(getClass.getSimpleName).toFile
try {
values0 ==== values
} finally {
dir.delete()
}
}

Property Plus Example#

The Scala version has an additional data type that allows generators to be applied to the final "test" in a way that can be invoked from by consumers.

def propReverse: PropertyR[List[Char]] =
PropertyR(
Gen.alpha.list(Range.linear(0, 100)).forAll
)(xs => xs.reverse.reverse ==== xs)

Here is an example of re-using the same method with both a property and a "golden" example test:

def tests: List[Test] =
List(
property(propReverse)
, example(propReverse.test(List('a', 'b', 'c')))
)

Monadic Gen#

One of the original goals of the Haskell implementation was to support completely generic monadic values.

Generators allow monadic effects.

For example you could use a StateT as part of the generator. In a strict language like Scala the Monad is also critical for providing a lazy tree. However, putting the laziness on each tree node results in serious memory problems. For now we have had to move this laziness to the tree children.

In practice I doubt that many people are seriously using monadic effects for generated values, and I'm happy to revisit this if/when an issue is raised.

State Vars#

The Haskell State testing uses a very powerful Symbolic and Concrete types to represent the different states of a variable when implementing the Command interface.

While this is technically possible in Scala, the types are quite intimidating, especially HTraversable. Instead we opt for a lower-level variable "lookup" by passing in the Environment map where concrete variables are available, so that users can resolve them manually.