Skip to content

Commit

Permalink
Merge pull request #162 from CodaFi/cogito-ergo-product
Browse files Browse the repository at this point in the history
Implement Monoidal Functor Instance for Gen
  • Loading branch information
CodaFi committed May 6, 2016
2 parents f0c8ac3 + 8cccf04 commit f01c188
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 25 deletions.
110 changes: 110 additions & 0 deletions Sources/Gen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,124 @@ public struct Gen<A> {
public static func weighted<S : SequenceType where S.Generator.Element == (Int, A)>(xs : S) -> Gen<A> {
return frequency(xs.map { ($0, Gen.pure($1)) })
}
}

extension Gen /*: Cartesian*/ {
/// Zips together 2 generators of type `A` and `B` into a generator of pairs.
public static func zip<A, B>(gen1 : Gen<A>, _ gen2 : Gen<B>) -> Gen<(A, B)> {
return Gen<(A, B)>(unGen: { r, n in
let (r1, r2) = r.split
return (gen1.unGen(r1, n), gen2.unGen(r2, n))
})
}

/// Zips together 3 generators of type `A`, `B`, and `C` into a generator of
/// triples.
public static func zip<A, B, C>(gen1 : Gen<A>, _ gen2 : Gen<B>, _ gen3 : Gen<C>) -> Gen<(A, B, C)> {
return Gen<(A, B, C)>(unGen: { r, n in
let (r1, r2_) = r.split
let (r2, r3) = r2_.split
return (gen1.unGen(r1, n), gen2.unGen(r2, n), gen3.unGen(r3, n))
})
}

/// Zips together 4 generators of type `A`, `B`, `C`, and `D` into a
/// generator of quadruples.
public static func zip<A, B, C, D>(gen1 : Gen<A>, _ gen2 : Gen<B>, _ gen3 : Gen<C>, _ gen4 : Gen<D>) -> Gen<(A, B, C, D)> {
return Gen<(A, B, C, D)>(unGen: { r, n in
let (r1_, r2_) = r.split
let (r1, r2) = r1_.split
let (r3, r4) = r2_.split
return (gen1.unGen(r1, n), gen2.unGen(r2, n), gen3.unGen(r3, n), gen4.unGen(r4, n))
})
}

/// Zips together 5 generators of type `A`, `B`, `C`, `D`, and `E` into a
/// generator of quintuples.
public static func zip<A, B, C, D, E>(gen1 : Gen<A>, _ gen2 : Gen<B>, _ gen3 : Gen<C>, _ gen4 : Gen<D>, _ gen5 : Gen<E>) -> Gen<(A, B, C, D, E)> {
return Gen<(A, B, C, D, E)>(unGen: { r, n in
let (r1_, r2_) = r.split
let (r1__, r1) = r1_.split
let (r2, r3) = r2_.split
let (r4, r5) = r1__.split
return (gen1.unGen(r1, n), gen2.unGen(r2, n), gen3.unGen(r3, n), gen4.unGen(r4, n), gen5.unGen(r5, n))
})
}

/// Zips together 6 generators of type `A`, `B`, `C`, `D` `E`, and `F` into
/// a generator of sextuples.
public static func zip<A, B, C, D, E, F>(gen1 : Gen<A>, _ gen2 : Gen<B>, _ gen3 : Gen<C>, _ gen4 : Gen<D>, _ gen5 : Gen<E>, _ gen6 : Gen<F>) -> Gen<(A, B, C, D, E, F)> {
return Gen<(A, B, C, D, E, F)>(unGen: { r, n in
let (r1_, r2_) = r.split
let (r1__, r2__) = r1_.split
let (r1, r2) = r2_.split
let (r3, r4) = r1__.split
let (r5, r6) = r2__.split
return (gen1.unGen(r1, n), gen2.unGen(r2, n), gen3.unGen(r3, n), gen4.unGen(r4, n), gen5.unGen(r5, n), gen6.unGen(r6, n))
})
}

/// Zips together 7 generators of type `A`, `B`, `C`, `D` `E`, `F`, and `G`
/// into a generator of septuples.
public static func zip<A, B, C, D, E, F, G>(gen1 : Gen<A>, _ gen2 : Gen<B>, _ gen3 : Gen<C>, _ gen4 : Gen<D>, _ gen5 : Gen<E>, _ gen6 : Gen<F>, _ gen7 : Gen<G>) -> Gen<(A, B, C, D, E, F, G)> {
return Gen<(A, B, C, D, E, F, G)>(unGen: { r, n in
let (r1_, r2_) = r.split
let (r1__, r2__) = r1_.split
let (r1___, r1) = r2_.split
let (r2, r3) = r1__.split
let (r4, r5) = r2__.split
let (r6, r7) = r1___.split
return (gen1.unGen(r1, n), gen2.unGen(r2, n), gen3.unGen(r3, n), gen4.unGen(r4, n), gen5.unGen(r5, n), gen6.unGen(r6, n), gen7.unGen(r7, n))
})
}

/// Zips together 8 generators of type `A`, `B`, `C`, `D` `E`, `F`, `G`, and
/// `H` into a generator of octuples.
public static func zip<A, B, C, D, E, F, G, H>(gen1 : Gen<A>, _ gen2 : Gen<B>, _ gen3 : Gen<C>, _ gen4 : Gen<D>, _ gen5 : Gen<E>, _ gen6 : Gen<F>, _ gen7 : Gen<G>, _ gen8 : Gen<H>) -> Gen<(A, B, C, D, E, F, G, H)> {
return Gen<(A, B, C, D, E, F, G, H)>(unGen: { r, n in
let (r1_, r2_) = r.split
let (r1__, r2__) = r1_.split
let (r1___, r2___) = r2_.split
let (r1, r2) = r1__.split
let (r3, r4) = r2__.split
let (r5, r6) = r1___.split
let (r7, r8) = r2___.split
return (gen1.unGen(r1, n), gen2.unGen(r2, n), gen3.unGen(r3, n), gen4.unGen(r4, n), gen5.unGen(r5, n), gen6.unGen(r6, n), gen7.unGen(r7, n), gen8.unGen(r8, n))
})
}

/// Zips together 9 generators of type `A`, `B`, `C`, `D` `E`, `F`, `G`,
/// `H`, and `I` into a generator of nonuples.
public static func zip<A, B, C, D, E, F, G, H, I>(gen1 : Gen<A>, _ gen2 : Gen<B>, _ gen3 : Gen<C>, _ gen4 : Gen<D>, _ gen5 : Gen<E>, _ gen6 : Gen<F>, _ gen7 : Gen<G>, _ gen8 : Gen<H>, _ gen9 : Gen<I>) -> Gen<(A, B, C, D, E, F, G, H, I)> {
return Gen<(A, B, C, D, E, F, G, H, I)>(unGen: { r, n in
let (r1_, r2_) = r.split
let (r1__, r2__) = r1_.split
let (r1___, r2___) = r2_.split
let (r1____, r1) = r1__.split
let (r2, r3) = r2__.split
let (r4, r5) = r1___.split
let (r6, r7) = r2___.split
let (r8, r9) = r1____.split
return (gen1.unGen(r1, n), gen2.unGen(r2, n), gen3.unGen(r3, n), gen4.unGen(r4, n), gen5.unGen(r5, n), gen6.unGen(r6, n), gen7.unGen(r7, n), gen8.unGen(r8, n), gen9.unGen(r9, n))
})
}

/// Zips together 10 generators of type `A`, `B`, `C`, `D` `E`, `F`, `G`,
/// `H`, `I`, and `J` into a generator of decuples.
public static func zip<A, B, C, D, E, F, G, H, I, J>(gen1 : Gen<A>, _ gen2 : Gen<B>, _ gen3 : Gen<C>, _ gen4 : Gen<D>, _ gen5 : Gen<E>, _ gen6 : Gen<F>, _ gen7 : Gen<G>, _ gen8 : Gen<H>, _ gen9 : Gen<I>, _ gen10 : Gen<J>) -> Gen<(A, B, C, D, E, F, G, H, I, J)> {
return Gen<(A, B, C, D, E, F, G, H, I, J)>(unGen: { r, n in
let (r1_, r2_) = r.split
let (r1__, r2__) = r1_.split
let (r1___, r2___) = r2_.split
let (r1____, r2____) = r1__.split
let (r1, r2) = r2__.split
let (r3, r4) = r1___.split
let (r5, r6) = r2___.split
let (r7, r8) = r1____.split
let (r9, r10) = r2____.split
return (gen1.unGen(r1, n), gen2.unGen(r2, n), gen3.unGen(r3, n), gen4.unGen(r4, n), gen5.unGen(r5, n), gen6.unGen(r6, n), gen7.unGen(r7, n), gen8.unGen(r8, n), gen9.unGen(r9, n), gen10.unGen(r10, n))
})
}
}

// MARK: Generator Modifiers
Expand Down
66 changes: 65 additions & 1 deletion Tests/GenSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,76 @@ class GenSpec : XCTestCase {
}
}

property("Gen.zip behaves") <- forAll { (x : Int, y : Int) in
property("Gen.zip2 behaves") <- forAll { (x : Int, y : Int) in
let g = Gen<(Int, Int)>.zip(Gen.pure(x), Gen.pure(y))
return forAllNoShrink(g) { (x1, y1) in
return (x1, y1) == (x, y)
}
}

property("Gen.zip3 behaves") <- forAll { (x : Int, y : Int, z : Int) in
let g = Gen<(Int, Int, Int)>.zip(Gen.pure(x), Gen.pure(y), Gen.pure(z))
return forAllNoShrink(g) { (x1, y1, z1) in
return (x1, y1, z1) == (x, y, z)
}
}

property("Gen.zip4 behaves") <- forAll { (x : Int, y : Int, z : Int, w : Int) in
let g = Gen<(Int, Int, Int, Int)>.zip(Gen.pure(x), Gen.pure(y), Gen.pure(z), Gen.pure(w))
return forAllNoShrink(g) { (x1, y1, z1, w1) in
return (x1, y1, z1, w1) == (x, y, z, w)
}
}

property("Gen.zip5 behaves") <- forAll { (x : Int, y : Int, z : Int, w : Int, a : Int) in
let g = Gen<(Int, Int, Int, Int, Int)>.zip(Gen.pure(x), Gen.pure(y), Gen.pure(z), Gen.pure(w), Gen.pure(a))
return forAllNoShrink(g) { (x1, y1, z1, w1, a1) in
return (x1, y1, z1, w1, a1) == (x, y, z, w, a)
}
}

property("Gen.zip6 behaves") <- forAll { (x : Int, y : Int, z : Int, w : Int, a : Int, b : Int) in
let g = Gen<(Int, Int, Int, Int, Int, Int)>.zip(Gen.pure(x), Gen.pure(y), Gen.pure(z), Gen.pure(w), Gen.pure(a), Gen.pure(b))
return forAllNoShrink(g) { (x1, y1, z1, w1, a1, b1) in
return (x1, y1, z1, w1, a1, b1) == (x, y, z, w, a, b)
}
}

property("Gen.zip7 behaves") <- forAll { (x : Int, y : Int, z : Int, w : Int, a : Int, b : Int, c : Int) in
let g = Gen<(Int, Int, Int, Int, Int, Int, Int)>.zip(Gen.pure(x), Gen.pure(y), Gen.pure(z), Gen.pure(w), Gen.pure(a), Gen.pure(b), Gen.pure(c))
return forAllNoShrink(g) { (x1, y1, z1, w1, a1, b1, c1) in
return (x1, y1, z1, w1, a1, b1) == (x, y, z, w, a, b)
&& c1 == c
}
}

property("Gen.zip8 behaves") <- forAll { (x : Int, y : Int, z : Int, w : Int, a : Int, b : Int, c : Int, d : Int) in
let g = Gen<(Int, Int, Int, Int, Int, Int)>.zip(Gen.pure(x), Gen.pure(y), Gen.pure(z), Gen.pure(w), Gen.pure(a), Gen.pure(b), Gen.pure(c), Gen.pure(d))
return forAllNoShrink(g) { (x1, y1, z1, w1, a1, b1, c1, d1) in
return (x1, y1, z1, w1, a1, b1) == (x, y, z, w, a, b)
&& (c1, d1) == (c, d)
}
}

property("Gen.zip9 behaves") <- forAll { (x : Int, y : Int, z : Int, w : Int, a : Int, b : Int, c : Int, d : Int) in
return forAll { (e : Int) in
let g = Gen<(Int, Int, Int, Int, Int, Int, Int, Int, Int)>.zip(Gen.pure(x), Gen.pure(y), Gen.pure(z), Gen.pure(w), Gen.pure(a), Gen.pure(b), Gen.pure(c), Gen.pure(d), Gen.pure(e))
return forAllNoShrink(g) { (x1, y1, z1, w1, a1, b1, c1, d1, e1) in
return (x1, y1, z1, w1, a1, b1) == (x, y, z, w, a, b)
&& (c1, d1, e1) == (c, d, e)
}
}
}

property("Gen.zip10 behaves") <- forAll { (x : Int, y : Int, z : Int, w : Int, a : Int, b : Int, c : Int, d : Int) in
return forAll { (e : Int, f : Int) in
let g = Gen<(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)>.zip(Gen.pure(x), Gen.pure(y), Gen.pure(z), Gen.pure(w), Gen.pure(a), Gen.pure(b), Gen.pure(c), Gen.pure(d), Gen.pure(e), Gen.pure(f))
return forAllNoShrink(g) { (x1, y1, z1, w1, a1, b1, c1, d1, e1, f1) in
return (x1, y1, z1, w1, a1, b1) == (x, y, z, w, a, b)
&& (c1, d1, e1, f1) == (c, d, e, f)
}
}
}
}

func testLaws() {
Expand Down
38 changes: 19 additions & 19 deletions Tests/RawRepresentableSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,33 @@ import XCTest
import SwiftCheck

enum ImplicitRawValues : Int {
case Foo
case Bar
case Baz
case Foo
case Bar
case Baz
}

// Declaring the extension allows Swift to know this particular enum can be Arbitrary
// ...but it doesn't need to be implemented since the protocol extension gives us a default implementation!
extension ImplicitRawValues: Arbitrary {}

enum ExplicitRawValues : Int {
case Zero = 0
case One = 1
case Two = 2
case Zero = 0
case One = 1
case Two = 2
}

class RawRepresentable_ArbitrarySpec: XCTestCase {
func testDefaultRawRepresentableGeneratorWithImplicitRawValues() {
property("only generates Foo, Bar, or Baz") <- forAll { (e: ImplicitRawValues) in
return [.Foo, .Bar, .Baz].contains(e)
}
}
func testDeafultRawRepresentableGeneratorWithExplicitRawValues() {
// when no extension is given, the user has to call `forAllNoShrink` since the compiler doesn't automatically
// infer protocol conformance
property("only generates Zero, One, or Two") <- forAllNoShrink(ExplicitRawValues.arbitrary) { e in
return [.Zero, .One, .Two].contains(e)
}
}
func testDefaultRawRepresentableGeneratorWithImplicitRawValues() {
property("only generates Foo, Bar, or Baz") <- forAll { (e: ImplicitRawValues) in
return [.Foo, .Bar, .Baz].contains(e)
}
}
func testDeafultRawRepresentableGeneratorWithExplicitRawValues() {
// when no extension is given, the user has to call `forAllNoShrink` since the compiler doesn't automatically
// infer protocol conformance
property("only generates Zero, One, or Two") <- forAllNoShrink(ExplicitRawValues.arbitrary) { e in
return [.Zero, .One, .Two].contains(e)
}
}
}
61 changes: 56 additions & 5 deletions Tests/SimpleSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,52 @@ public struct ArbitraryFoo {
let x : Int
let y : Int

public static func create(x : Int) -> Int -> ArbitraryFoo {
return { y in ArbitraryFoo(x: x, y: y) }
}

public var description : String {
return "Arbitrary Foo!"
}
}

extension ArbitraryFoo : Arbitrary {
public static var arbitrary : Gen<ArbitraryFoo> {
return ArbitraryFoo.create <^> Int.arbitrary <*> Int.arbitrary
return Gen<(Int, Int)>.zip(Int.arbitrary, Int.arbitrary).map(ArbitraryFoo.init)
}
}

public struct ArbitraryLargeFoo {
let a : Int8
let b : Int16
let c : Int32
let d : Int64
let e : UInt8
let f : UInt16
let g : UInt32
let h : UInt64
let i : Int
let j : UInt
let k : Bool
let l : (Bool, Bool)
let m : (Bool, Bool, Bool)
let n : (Bool, Bool, Bool, Bool)
}

extension ArbitraryLargeFoo : Arbitrary {
public static var arbitrary : Gen<ArbitraryLargeFoo> {
return Gen<(Int8, Int16, Int32, Int64
, UInt8, UInt16, UInt32, UInt64
, Int , UInt)>
.zip( Int8.arbitrary, Int16.arbitrary, Int32.arbitrary, Int64.arbitrary
, UInt8.arbitrary, UInt16.arbitrary, UInt32.arbitrary, UInt64.arbitrary
, Int.arbitrary, UInt.arbitrary)
.flatMap { t in
return Gen<(Bool, (Bool, Bool), (Bool, Bool, Bool), (Bool, Bool, Bool, Bool))>
.zip( Bool.arbitrary
, Gen<(Bool, Bool)>.zip(Bool.arbitrary, Bool.arbitrary)
, Gen<(Bool, Bool, Bool)>.zip(Bool.arbitrary, Bool.arbitrary, Bool.arbitrary)
, Gen<(Bool, Bool, Bool, Bool)>.zip(Bool.arbitrary, Bool.arbitrary, Bool.arbitrary, Bool.arbitrary))
.map({ t2 in
return (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, t.8, t.9, t2.0, t2.1, t2.2, t2.3)
}).map(ArbitraryLargeFoo.init)
}
}
}

Expand Down Expand Up @@ -54,6 +88,23 @@ class SimpleSpec : XCTestCase {
return i.x == i.x && i.y == i.y
}

property("ArbitraryLargeFoo Properties are Reflexive") <- forAll { (i : ArbitraryLargeFoo) in
return i.a == i.a
&& i.b == i.b
&& i.c == i.c
&& i.d == i.d
&& i.e == i.e
&& i.f == i.f
&& i.g == i.g
&& i.h == i.h
&& i.i == i.i
&& i.j == i.j
&& i.k == i.k
&& i.l == i.l
&& i.m == i.m
&& i.n == i.n
}

property("All generated Charaters are valid Unicode") <- forAll { (c : Character) in
return
(c >= ("\u{0000}" as Character) && c <= ("\u{D7FF}" as Character))
Expand Down

0 comments on commit f01c188

Please sign in to comment.