hedgehogqa / hedgehogqa/fsharp-hedgehog

Unifying `Property<unit>` and `Property<bool>`

Open
#365 5 comments 0 reactions 0 assignees View on GitHub
Dominant language
F#
Stars
284
Forks
31
PR merge metrics
No merged PRs in 30d

Description

I've been looking through Haskell Hedgehog recently, and I noticed that their `Property` type is not polymorphic. I'd like to get the F# version in line with this, and I have some ideas on how this might be done, though I'm very open to ideas/suggestions here.

```fsi
module Property =
/// Turns a generator into a property, verifying that the condition always holds.
val always : ('a -> bool) -> Gen<'a> -> Property

/// Turns a bool generator into a property, verifying that the generator always generates `true`.
val alwaysTrue : Gen -> Property

/// Turns a bool generator into a property, verifying that the generator always generates `false`.
val alwaysFalse : Gen -> Property

/// Turns a `Result` generator into a property, verifying that the generator always generates `Ok`.
val alwaysOk : Gen> -> Property

/// Turns a `Result` generator into a property, verifying that the generator always generates `Error`.
val alwaysError : Gen> -> Property

/// Turns a unit generator into a property, verifying that the generator always throws a specific exception.
/// This could resolve `#128`.
val alwaysThrowsExact<'exn :> exn> : Gen -> Property

/// Turns a unit generator into a property, verifying that the generator always throws an exception.
val alwaysThrows : Gen -> Property

/// Turns a unit generator into a property, verifying that the generator never throws an exception.
/// Alias for Property.forAll?
val neverThrows : Gen -> Property
```

This API would be used like so:

```fsharp
gen {
let! x = Gen.bool
Expect.equal x x "x = x"
}
|> Property.neverThrows
|> Property.check

gen {
let! x = Gen.bool
return x = x
}
|> Property.alwaysTrue
|> Property.check

gen {
raise (NotImplementedException())
}
|> Property.alwaysThrowsExact
|> Property.check

gen {
let! z = Gen.int32 (Range.constantBounded ())
return z
}
|> Property.always (fun x -> x <> 0)
|> Property.check
```

I'm still unsure how we would support `counterexample` and maybe another function or two with this, mostly because I haven't dug into them yet.

Contributor guide

Open the contributing guide

Research direction

Start by reading the existing Property API and comparing the current Property and Property behavior. Resolve the proposed polymorphic shape and how counterexample support fits, then define the completion criteria for the always, alwaysTrue, alwaysFalse, Result, and exception helpers before implementing or testing the API.

Written by the indexing model from the issue text.

Assessment

Tech stack
fsharp
Domain
testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.