Skip to main content

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.