Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rearrange parameters for better chaining. #266

Merged
merged 8 commits into from Jan 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 40 additions & 34 deletions src/Hedgehog/Gen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ module Gen =
let delay (f : unit -> Gen<'a>) : Gen<'a> =
Random.delay (toRandom << f) |> ofRandom

let tryFinally (m : Gen<'a>) (after : unit -> unit) : Gen<'a> =
Random.tryFinally (toRandom m) after |> ofRandom
let tryFinally (after : unit -> unit) (m : Gen<'a>) : Gen<'a> =
toRandom m |> Random.tryFinally after |> ofRandom

let tryWith (m : Gen<'a>) (k : exn -> Gen<'a>) : Gen<'a> =
Random.tryWith (toRandom m) (toRandom << k) |> ofRandom
let tryWith (k : exn -> Gen<'a>) (m : Gen<'a>) : Gen<'a> =
toRandom m |> Random.tryWith (toRandom << k) |> ofRandom

let create (shrink : 'a -> seq<'a>) (random : Random<'a>) : Gen<'a> =
Random.map (Tree.unfold id shrink) random |> ofRandom
random |> Random.map (Tree.unfold id shrink) |> ofRandom

let constant (x : 'a) : Gen<'a> =
Tree.singleton x |> Random.constant |> ofRandom

let private bindRandom (m : Random<Tree<'a>>) (k : 'a -> Random<Tree<'b>>) : Random<Tree<'b>> =
let private bindRandom (k : 'a -> Random<Tree<'b>>) (m : Random<Tree<'a>>) : Random<Tree<'b>> =
Hedgehog.Random (fun seed0 size ->
let seed1, seed2 =
Seed.split seed0
Expand All @@ -40,12 +40,12 @@ module Gen =

Tree.bind (run seed1 m) (run seed2 << k))

let bind (m0 : Gen<'a>) (k0 : 'a -> Gen<'b>) : Gen<'b> =
bindRandom (toRandom m0) (toRandom << k0) |> ofRandom
let bind (k : 'a -> Gen<'b>) (m : Gen<'a>) : Gen<'b> =
toRandom m |> bindRandom (toRandom << k) |> ofRandom

let apply (gf : Gen<'a -> 'b>) (gx : Gen<'a>) : Gen<'b> =
bind gf (fun f ->
bind gx (f >> constant))
let apply (gx : Gen<'a>) (gf : Gen<'a -> 'b>) : Gen<'b> =
gf |> bind (fun f ->
gx |> bind (f >> constant))

let mapRandom (f : Random<Tree<'a>> -> Random<Tree<'b>>) (g : Gen<'a>) : Gen<'b> =
toRandom g |> f |> ofRandom
Expand All @@ -57,21 +57,21 @@ module Gen =
mapTree (Tree.map f) g

let map2 (f : 'a -> 'b -> 'c) (gx : Gen<'a>) (gy : Gen<'b>) : Gen<'c> =
bind gx (fun x ->
bind gy (fun y ->
gx |> bind (fun x ->
gy |> bind (fun y ->
constant (f x y)))

let map3 (f : 'a -> 'b -> 'c -> 'd) (gx : Gen<'a>) (gy : Gen<'b>) (gz : Gen<'c>) : Gen<'d> =
bind gx (fun x ->
bind gy (fun y ->
bind gz (fun z ->
gx |> bind (fun x ->
gy |> bind (fun y ->
gz |> bind (fun z ->
constant (f x y z))))

let map4 (f : 'a -> 'b -> 'c -> 'd -> 'e) (gx : Gen<'a>) (gy : Gen<'b>) (gz : Gen<'c>) (gw : Gen<'d>) : Gen<'e> =
bind gx (fun x ->
bind gy (fun y ->
bind gz (fun z ->
bind gw (fun w ->
gx |> bind (fun x ->
gy |> bind (fun y ->
gz |> bind (fun z ->
gw |> bind (fun w ->
constant (f x y z w)))))

let zip (gx : Gen<'a>) (gy : Gen<'b>) : Gen<'a * 'b> =
Expand All @@ -95,24 +95,24 @@ module Gen =
type Builder internal () =
let rec loop p m =
if p () then
bind m (fun _ -> loop p m)
m |> bind (fun _ -> loop p m)
else
constant ()

member __.Return(a) =
member __.Return(a) : Gen<'a> =
constant a
member __.ReturnFrom(g) =
member __.ReturnFrom(g) : Gen<'a> =
g
member __.Bind(m, k) =
bind m k
m |> bind k
member __.For(xs, k) =
let xse = (xs :> seq<'a>).GetEnumerator ()
using xse (fun xse ->
let mv = xse.MoveNext
let kc = delay (fun () -> k xse.Current)
loop mv kc)
member __.Combine(m, n) =
bind m (fun () -> n)
m |> bind (fun () -> n)
member __.Delay(f) =
delay f
member __.Zero() =
Expand Down Expand Up @@ -252,7 +252,7 @@ module Gen =
Random.constant None
| n ->
let r = Random.resize (2 * k + n) r0
Random.bind r (fun x ->
r |> Random.bind (fun x ->
if p (Tree.outcome x) then
Tree.filter p x |> Some |> Random.constant
else
Expand All @@ -263,7 +263,9 @@ module Gen =
/// Generates a value that satisfies a predicate.
let filter (p : 'a -> bool) (g : Gen<'a>) : Gen<'a> =
let rec loop () =
Random.bind (toRandom g |> tryFilterRandom p) (function
toRandom g
|> tryFilterRandom p
|> Random.bind (function
| None ->
Random.sized (fun n ->
Random.resize (n + 1) (Random.delay loop))
Expand All @@ -277,12 +279,16 @@ module Gen =
let tryFilter (p : 'a -> bool) (g : Gen<'a>) : Gen<'a option> =
toRandom g
|> tryFilterRandom p
|> flip Random.bind (OptionTree.sequence >> Random.constant)
|> Random.bind (OptionTree.sequence >> Random.constant)
|> ofRandom

/// Runs an option generator until it produces a 'Some'.
let some (g : Gen<'a option>) : Gen<'a> =
bind (filter Option.isSome g) (Option.get >> constant)
filter Option.isSome g |> bind (function
| Some x ->
constant x
| None ->
invalidOp "internal error, unexpected None")

//
// Combinators - Collections
Expand Down Expand Up @@ -506,11 +512,11 @@ module Gen =
printfn "%A" (Tree.outcome shrink)
printfn "."

module Operators =
let (<!>) f g = map f g
let (<*>) gf g = apply g gf
let (>>=) g f = bind f g

[<AutoOpen>]
module GenBuilder =
let gen = Gen.Builder ()

[<AutoOpen>]
module GenOperators =
let (<!>) = Gen.map
let (<*>) = Gen.apply
7 changes: 7 additions & 0 deletions src/Hedgehog/GenTuple.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module private Hedgehog.GenTuple

let mapFst (f : 'a -> 'c) (gen : Gen<'a * 'b>) : Gen<'c * 'b> =
Gen.map (Tuple.mapFst f) gen

let mapSnd (f : 'b -> 'c) (gen : Gen<'a * 'b>) : Gen<'a * 'c> =
Gen.map (Tuple.mapSnd f) gen
1 change: 1 addition & 0 deletions src/Hedgehog/Hedgehog.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ https://github.com/hedgehogqa/fsharp-hedgehog/blob/master/doc/tutorial.md
<Compile Include="ListGen.fs" />
<Compile Include="Journal.fs" />
<Compile Include="Tuple.fs" />
<Compile Include="GenTuple.fs" />
<Compile Include="Outcome.fs" />
<Compile Include="Report.fs" />
<Compile Include="Property.fs" />
Expand Down
12 changes: 4 additions & 8 deletions src/Hedgehog/Linq/Gen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ type GenExtensions private () =

[<Extension>]
static member Apply (genFunc : Gen<Func<'T, 'TResult>>, genArg : Gen<'T>) : Gen<'TResult> =
Gen.apply (genFunc |> Gen.map (fun f -> f.Invoke)) genArg
Gen.apply genArg (genFunc |> Gen.map (fun f -> f.Invoke))

[<Extension>]
static member Array (gen : Gen<'T>, range : Range<int>) : Gen<'T []> =
Expand Down Expand Up @@ -201,7 +201,7 @@ type GenExtensions private () =

[<Extension>]
static member SelectMany (gen : Gen<'T>, binder : Func<'T, Gen<'U>>) : Gen<'U> =
Gen.bind gen binder.Invoke
Gen.bind binder.Invoke gen

[<Extension>]
static member SelectMany (gen : Gen<'T>, binder : Func<'T, Gen<'TCollection>>, projection : Func<'T, 'TCollection, 'TResult>) : Gen<'TResult> =
Expand Down Expand Up @@ -270,15 +270,11 @@ type GenExtensions private () =

[<Extension>]
static member TryFinally (gen : Gen<'T>, after : Action) : Gen<'T> =
Gen.tryFinally gen after.Invoke

[<Extension>]
static member TryWhere (gen : Gen<'T>, after : Func<exn, Gen<'T>>) : Gen<'T> =
Gen.tryWith gen after.Invoke
Gen.tryFinally after.Invoke gen

[<Extension>]
static member TryWith (gen : Gen<'T>, after : Func<exn, Gen<'T>>) : Gen<'T> =
Gen.tryWith gen after.Invoke
Gen.tryWith after.Invoke gen

[<Extension>]
static member Tuple2 (gen : Gen<'T>) : Gen<'T * 'T> =
Expand Down
17 changes: 9 additions & 8 deletions src/Hedgehog/Linq/Property.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type Property = private Property of Property<unit> with
|> Property

static member ForAll (gen : Gen<'T>, k : Func<'T, Property<'TResult>>) : Property<'TResult> =
Property.forAll gen k.Invoke
Property.forAll k.Invoke gen

static member ForAll (gen : Gen<'T>) : Property<'T> =
Property.forAll' gen
Expand All @@ -59,11 +59,11 @@ type PropertyExtensions private () =

[<Extension>]
static member TryFinally (property : Property<'T>, onFinally : Action) : Property<'T> =
Property.tryFinally property onFinally.Invoke
Property.tryFinally onFinally.Invoke property

[<Extension>]
static member TryWith (property : Property<'T>, onError : Func<exn, Property<'T>>) : Property<'T> =
Property.tryWith property onError.Invoke
Property.tryWith onError.Invoke property

//
// Runner
Expand Down Expand Up @@ -161,19 +161,20 @@ type PropertyExtensions private () =

[<Extension>]
static member Select (property : Property<'T>, mapper : Action<'T>) : Property =
Property.bind property (Property.ofThrowing mapper.Invoke)
property
|> Property.bind (Property.ofThrowing mapper.Invoke)
|> Property

[<Extension>]
static member SelectMany (property : Property<'T>, binder : Func<'T, Property<'TCollection>>, projection : Func<'T, 'TCollection, 'TResult>) : Property<'TResult> =
Property.bind property (fun a ->
Property.map (fun b -> projection.Invoke (a, b)) (binder.Invoke a))
property |> Property.bind (fun a ->
binder.Invoke a |> Property.map (fun b -> projection.Invoke (a, b)))

[<Extension>]
static member SelectMany (property : Property<'T>, binder : Func<'T, Property<'TCollection>>, projection : Action<'T, 'TCollection>) : Property =
let result =
Property.bind property (fun a ->
Property.bind (binder.Invoke a) (fun b ->
property |> Property.bind (fun a ->
binder.Invoke a |> Property.bind (fun b ->
Property.ofThrowing projection.Invoke (a, b)))
Property result

Expand Down
28 changes: 13 additions & 15 deletions src/Hedgehog/ListGen.fs
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
namespace Hedgehog
module Hedgehog.ListGen

module ListGen =
let traverse (f: 'a -> Gen<'b>) (ma: list<'a>) : Gen<list<'b>> =
let rec loop input output =
match input with
| [] -> output |> List.rev |> Gen.constant
| a :: input ->
gen {
let! b = f a
return! loop input (b :: output)
}
loop ma []

let traverse (f: 'a -> Gen<'b>) (ma: list<'a>) : Gen<list<'b>> =
let rec loop input output =
match input with
| [] -> output |> List.rev |> Gen.constant
| a :: input ->
gen {
let! b = f a
return! loop input (b :: output)
}
loop ma []

let sequence (gens : List<Gen<'a>>) : Gen<List<'a>> =
gens |> traverse id
let sequence (gens : List<Gen<'a>>) : Gen<List<'a>> =
gens |> traverse id
Loading