Skip to content

Commit 4863fa9

Browse files
committed
Merge pull request #207 from CodaFi/lawful-arrest
Test More Laws
2 parents 0036c1f + 71df044 commit 4863fa9

File tree

9 files changed

+146
-79
lines changed

9 files changed

+146
-79
lines changed

Cartfile.resolved

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
github "typelift/SwiftCheck" "v0.2.3"
1+
github "typelift/SwiftCheck" "v0.2.6"
22
github "typelift/Swiftx" "v0.2.1"

SwiftzTests/ArrayExtSpec.swift

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,61 @@ class ArrayExtSpec : XCTestCase {
2727
return indexArray(l.getArray, 0) != nil
2828
}
2929

30-
property["array fmap is the same as map"] = forAll { (xs : ArrayOf<Int>) in
30+
property["Array fmap is the same as map"] = forAll { (xs : ArrayOf<Int>) in
3131
return (+1 <^> xs.getArray) == xs.getArray.map(+1)
3232
}
3333

34-
property["array pure give a singleton array"] = forAll { (x : Int) in
34+
property["Array pure give a singleton array"] = forAll { (x : Int) in
3535
return pure(x) == [x]
3636
}
3737

38-
property["array bind works like a map then a concat"] = forAll { (xs : ArrayOf<Int>) in
38+
property["Array bind works like a map then a concat"] = forAll { (xs : ArrayOf<Int>) in
3939
func fs(x : Int) -> [Int] {
4040
return [x, x+1, x+2]
4141
}
4242

4343
return (xs.getArray >>- fs) == xs.getArray.map(fs).reduce([], combine: +)
4444
}
4545

46+
property["Array obeys the Functor identity law"] = forAll { (x : ArrayOf<Int>) in
47+
return (x.getArray.map(identity)) == identity(x.getArray)
48+
}
49+
50+
property["Array obeys the Functor composition law"] = forAll { (f : ArrowOf<Int, Int>, g : ArrowOf<Int, Int>, x : ArrayOf<Int>) in
51+
return ((f.getArrow g.getArrow) <^> x.getArray) == (x.getArray.map(g.getArrow).map(f.getArrow))
52+
}
53+
54+
property["Array obeys the Applicative identity law"] = forAll { (x : ArrayOf<Int>) in
55+
return (pure(identity) <*> x.getArray) == x.getArray
56+
}
57+
58+
reportProperty["Array obeys the first Applicative composition law"] = forAll { (fl : ArrayOf<ArrowOf<Int8, Int8>>, gl : ArrayOf<ArrowOf<Int8, Int8>>, x : ArrayOf<Int8>) in
59+
let f = fl.getArray.map({ $0.getArrow })
60+
let g = gl.getArray.map({ $0.getArrow })
61+
return (curry() <^> f <*> g <*> x.getArray) == (f <*> (g <*> x.getArray))
62+
}
63+
64+
reportProperty["Array obeys the second Applicative composition law"] = forAll { (fl : ArrayOf<ArrowOf<Int8, Int8>>, gl : ArrayOf<ArrowOf<Int8, Int8>>, x : ArrayOf<Int8>) in
65+
let f = fl.getArray.map({ $0.getArrow })
66+
let g = gl.getArray.map({ $0.getArrow })
67+
return (pure(curry()) <*> f <*> g <*> x.getArray) == (f <*> (g <*> x.getArray))
68+
}
69+
70+
property["Array obeys the Monad left identity law"] = forAll { (a : Int, fa : ArrowOf<Int, ArrayOf<Int>>) in
71+
let f = { $0.getArray } fa.getArrow
72+
return (pure(a) >>- f) == f(a)
73+
}
74+
75+
property["Array obeys the Monad right identity law"] = forAll { (m : ArrayOf<Int>) in
76+
return (m.getArray >>- pure) == m.getArray
77+
}
78+
79+
property["Array obeys the Monad associativity law"] = forAll { (fa : ArrowOf<Int, ArrayOf<Int>>, ga : ArrowOf<Int, ArrayOf<Int>>, m : ArrayOf<Int>) in
80+
let f = { $0.getArray } fa.getArrow
81+
let g = { $0.getArray } ga.getArrow
82+
return ((m.getArray >>- f) >>- g) == (m.getArray >>- { x in f(x) >>- g })
83+
}
84+
4685
property["scanl behaves"] = forAll { (withArray : ArrayOf<Int>) in
4786
let scanned = scanl(0, withArray.getArray, +)
4887
if withArray.getArray.isEmpty {

SwiftzTests/EitherSpec.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,7 @@ class EitherSpec : XCTestCase {
5858
}
5959

6060
property["fold returns its default on .Left"] = forAll { (e : EitherOf<String, Int>) in
61-
if e.getEither.isLeft() {
62-
return e.getEither.fold(0, f: identity) == 0
63-
} else {
64-
return true // discard
65-
}
61+
return e.getEither.isLeft() ==> (e.getEither.fold(0, f: identity) == 0)
6662
}
6763

6864
property["Either is a bifunctor"] = forAll { (e : EitherOf<String, Int>) in

SwiftzTests/FunctorSpec.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class FunctorSpec : XCTestCase {
1717
return (x.fmap(identity)).runConst == identity(x).runConst
1818
}
1919

20-
reportProperty["Const obeys the Functor composition law"] = forAll { (f : ArrowOf<Int, Int>, g : ArrowOf<Int, Int>) in
20+
property["Const obeys the Functor composition law"] = forAll { (f : ArrowOf<Int, Int>, g : ArrowOf<Int, Int>) in
2121
let x = Const<Int, Int>(5)
2222
return (x.fmap(f.getArrow g.getArrow)).runConst == (x.fmap(g.getArrow).fmap(f.getArrow)).runConst
2323
}
@@ -28,7 +28,7 @@ class FunctorSpec : XCTestCase {
2828
return t.runConst == identity(x).runConst
2929
}
3030

31-
reportProperty["Const obeys the Biunctor composition law"] = forAll { (f1 : ArrowOf<Int, Int>, g1 : ArrowOf<Int, Int>, f2 : ArrowOf<Int, Int>, g2 : ArrowOf<Int, Int>) in
31+
property["Const obeys the Biunctor composition law"] = forAll { (f1 : ArrowOf<Int, Int>, g1 : ArrowOf<Int, Int>, f2 : ArrowOf<Int, Int>, g2 : ArrowOf<Int, Int>) in
3232
let x = Const<Int, Int>(5)
3333
return x.bimap(f1.getArrow, g1.getArrow).bimap(f2.getArrow, g2.getArrow).runConst == (x.bimap(f2.getArrow f1.getArrow, g1.getArrow g2.getArrow)).runConst
3434
}

SwiftzTests/ListSpec.swift

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ class ListSpec : XCTestCase {
6262
}
6363

6464
property["Lists of Equatable elements obey transitivity"] = forAll { (x : ListOf<Int>, y : ListOf<Int>, z : ListOf<Int>) in
65-
if (x == y) && (y == z) {
66-
return x == z
67-
}
68-
return true // discard
65+
return (x == y) && (y == z) ==> (x == z)
6966
}
7067

7168
property["Lists of Equatable elements obey negation"] = forAll { (x : ListOf<Int>, y : ListOf<Int>) in
@@ -80,25 +77,26 @@ class ListSpec : XCTestCase {
8077
return (x.getList.fmap(identity)) == identity(x.getList)
8178
}
8279

83-
reportProperty["List obeys the Functor composition law"] = forAll { (f : ArrowOf<Int, Int>, g : ArrowOf<Int, Int>, x : ListOf<Int>) in
84-
return ((f.getArrow g.getArrow) <^> x.getList) == (x.getList.fmap(f.getArrow).fmap(g.getArrow))
80+
property["List obeys the Functor composition law"] = forAll { (f : ArrowOf<Int, Int>, g : ArrowOf<Int, Int>, x : ListOf<Int>) in
81+
return ((f.getArrow g.getArrow) <^> x.getList) == (x.getList.fmap(g.getArrow).fmap(f.getArrow))
8582
}
8683

8784
property["List obeys the Applicative identity law"] = forAll { (x : ListOf<Int>) in
8885
return (List.pure(identity) <*> x.getList) == x.getList
8986
}
9087

91-
reportProperty["List obeys the first Applicative composition law"] = forAll { (fl : ListOf<ArrowOf<Int8, Int8>>, gl : ListOf<ArrowOf<Int8, Int8>>, x : ListOf<Int8>) in
92-
let f = fl.getList.fmap({ $0.getArrow })
93-
let g = gl.getList.fmap({ $0.getArrow })
94-
return (curry() <^> f <*> g <*> x.getList) == (f <*> (g <*> x.getList))
95-
}
96-
97-
reportProperty["List obeys the second Applicative composition law"] = forAll { (fl : ListOf<ArrowOf<Int8, Int8>>, gl : ListOf<ArrowOf<Int8, Int8>>, x : ListOf<Int8>) in
98-
let f = fl.getList.fmap({ $0.getArrow })
99-
let g = gl.getList.fmap({ $0.getArrow })
100-
return (List.pure(curry()) <*> f <*> g <*> x.getList) == (f <*> (g <*> x.getList))
101-
}
88+
// Swift unroller can't handle our scale.
89+
// property["List obeys the first Applicative composition law"] = forAll { (fl : ListOf<ArrowOf<Int8, Int8>>, gl : ListOf<ArrowOf<Int8, Int8>>, x : ListOf<Int8>) in
90+
// let f = fl.getList.fmap({ $0.getArrow })
91+
// let g = gl.getList.fmap({ $0.getArrow })
92+
// return (curry(•) <^> f <*> g <*> x.getList) == (f <*> (g <*> x.getList))
93+
// }
94+
//
95+
// property["List obeys the second Applicative composition law"] = forAll { (fl : ListOf<ArrowOf<Int8, Int8>>, gl : ListOf<ArrowOf<Int8, Int8>>, x : ListOf<Int8>) in
96+
// let f = fl.getList.fmap({ $0.getArrow })
97+
// let g = gl.getList.fmap({ $0.getArrow })
98+
// return (List.pure(curry(•)) <*> f <*> g <*> x.getList) == (f <*> (g <*> x.getList))
99+
// }
102100

103101
property["List obeys the Monoidal left identity law"] = forAll { (x : ListOf<Int8>) in
104102
return (x.getList + List()) == x.getList
@@ -109,18 +107,14 @@ class ListSpec : XCTestCase {
109107
}
110108

111109
property["List can cycle into an infinite list"] = forAll { (x : ListOf<Int8>) in
112-
if x.getList.isEmpty() {
113-
return rejected()
114-
}
110+
return !x.getList.isEmpty() ==> {
111+
let finite = x.getList
112+
let cycle = finite.cycle()
115113

116-
let finite = x.getList
117-
let cycle = finite.cycle()
118-
for i : UInt in (0...100) {
119-
if cycle[i] != finite[(i % finite.length())] {
120-
return false
114+
return forAll { (n : Positive<Int>) in
115+
return (0...UInt(n.getPositive)).map({ i in cycle[i] == finite[(i % finite.length())] }).filter(==false).isEmpty
121116
}
122117
}
123-
return true
124118
}
125119

126120
property["isEmpty behaves"] = forAll { (xs : ListOf<Int>) in
@@ -136,9 +130,9 @@ class ListSpec : XCTestCase {
136130
return (xs.getList >>- fs) == xs.getList.map(fs).reduce(+, initial: List())
137131
}
138132

139-
// property["filter behaves"] = forAll { (xs : ListOf<Int>, pred : ArrowOf<Int, Bool>) in
140-
// return xs.getList.filter(pred.getArrow).reduce({ $0.0 && pred.getArrow($0.1) }, initial: true)
141-
// }
133+
property["filter behaves"] = forAll { (xs : ListOf<Int>, pred : ArrowOf<Int, Bool>) in
134+
return xs.getList.filter(pred.getArrow).reduce({ $0.0 && pred.getArrow($0.1) }, initial: true)
135+
}
142136

143137
property["take behaves"] = forAll { (xs : ListOf<Int>, limit : UInt) in
144138
return xs.getList.take(limit).length() <= limit

SwiftzTests/MaybeSpec.swift

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,7 @@ class MaybeSpec : XCTestCase {
5959
}
6060

6161
property["Maybes of Equatable elements obey transitivity"] = forAll { (x : MaybeOf<Int>, y : MaybeOf<Int>, z : MaybeOf<Int>) in
62-
if (x == y) && (y == z) {
63-
return x == z
64-
}
65-
return true // discard
62+
return (x == y) && (y == z) ==> (x == z)
6663
}
6764

6865
property["Maybes of Equatable elements obey negation"] = forAll { (x : MaybeOf<Int>, y : MaybeOf<Int>) in
@@ -77,24 +74,39 @@ class MaybeSpec : XCTestCase {
7774
return (x.getMaybe.fmap(identity)) == identity(x.getMaybe)
7875
}
7976

80-
reportProperty["Maybe obeys the Functor composition law"] = forAll { (f : ArrowOf<Int, Int>, g : ArrowOf<Int, Int>, x : MaybeOf<Int>) in
81-
return ((f.getArrow g.getArrow) <^> x.getMaybe) == (x.getMaybe.fmap(f.getArrow).fmap(g.getArrow))
77+
property["Maybe obeys the Functor composition law"] = forAll { (f : ArrowOf<Int, Int>, g : ArrowOf<Int, Int>, x : MaybeOf<Int>) in
78+
return ((f.getArrow g.getArrow) <^> x.getMaybe) == (x.getMaybe.fmap(g.getArrow).fmap(f.getArrow))
8279
}
8380

8481
property["Maybe obeys the Applicative identity law"] = forAll { (x : MaybeOf<Int>) in
8582
return (Maybe.pure(identity) <*> x.getMaybe) == x.getMaybe
8683
}
8784

88-
reportProperty["Maybe obeys the first Applicative composition law"] = forAll { (fl : MaybeOf<ArrowOf<Int8, Int8>>, gl : MaybeOf<ArrowOf<Int8, Int8>>, x : MaybeOf<Int8>) in
85+
property["Maybe obeys the first Applicative composition law"] = forAll { (fl : MaybeOf<ArrowOf<Int8, Int8>>, gl : MaybeOf<ArrowOf<Int8, Int8>>, x : MaybeOf<Int8>) in
8986
let f = fl.getMaybe.fmap({ $0.getArrow })
9087
let g = gl.getMaybe.fmap({ $0.getArrow })
9188
return (curry() <^> f <*> g <*> x.getMaybe) == (f <*> (g <*> x.getMaybe))
9289
}
9390

94-
reportProperty["Maybe obeys the second Applicative composition law"] = forAll { (fl : MaybeOf<ArrowOf<Int8, Int8>>, gl : MaybeOf<ArrowOf<Int8, Int8>>, x : MaybeOf<Int8>) in
91+
property["Maybe obeys the second Applicative composition law"] = forAll { (fl : MaybeOf<ArrowOf<Int8, Int8>>, gl : MaybeOf<ArrowOf<Int8, Int8>>, x : MaybeOf<Int8>) in
9592
let f = fl.getMaybe.fmap({ $0.getArrow })
9693
let g = gl.getMaybe.fmap({ $0.getArrow })
9794
return (Maybe.pure(curry()) <*> f <*> g <*> x.getMaybe) == (f <*> (g <*> x.getMaybe))
9895
}
96+
97+
property["Maybe obeys the Monad left identity law"] = forAll { (a : Int, fa : ArrowOf<Int, MaybeOf<Int>>) in
98+
let f = { $0.getMaybe } fa.getArrow
99+
return (Maybe.pure(a) >>- f) == f(a)
100+
}
101+
102+
property["Maybe obeys the Monad right identity law"] = forAll { (m : MaybeOf<Int>) in
103+
return (m.getMaybe >>- Maybe.pure) == m.getMaybe
104+
}
105+
106+
property["Maybe obeys the Monad associativity law"] = forAll { (fa : ArrowOf<Int, MaybeOf<Int>>, ga : ArrowOf<Int, MaybeOf<Int>>, m : MaybeOf<Int>) in
107+
let f = { $0.getMaybe } fa.getArrow
108+
let g = { $0.getMaybe } ga.getArrow
109+
return ((m.getMaybe >>- f) >>- g) == (m.getMaybe >>- { x in f(x) >>- g })
110+
}
99111
}
100112
}

SwiftzTests/OptionalExtSpec.swift

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,50 @@ class OptionalExtSpec : XCTestCase {
5050
}
5151
return maybe(x.getOptional)(0)(+1) == 0
5252
}
53-
}
5453

55-
func testOptionalExt() {
56-
let x = Optional.Some(4)
57-
let y = Optional<Int>.None
54+
property["Optional obeys the Functor identity law"] = forAll { (x : OptionalOf<Int>) in
55+
return (x.getOptional.map(identity)) == identity(x.getOptional)
56+
}
5857

59-
XCTAssert((x >>- { i in
60-
if i % 2 == 0 {
61-
return .Some(i / 2)
62-
} else {
63-
return .None
64-
}
65-
}) == Optional.Some(2), "optional flatMap")
58+
property["Optional obeys the Functor composition law"] = forAll { (f : ArrowOf<Int, Int>, g : ArrowOf<Int, Int>, x : OptionalOf<Int>) in
59+
return ((f.getArrow g.getArrow) <^> x.getOptional) == (x.getOptional.map(g.getArrow).map(f.getArrow))
60+
}
6661

67-
/// Forbidden by Swift 1.2; see ~( http://stackoverflow.com/a/29750368/945847 ))
68-
// XCTAssert(coalesce(x, y) == 4, "coalesce some first")
69-
// XCTAssert(coalesce(y, x) == 4, "coalesce some second")
70-
// XCTAssert(coalesce({ n in n > 4 })(y, x) == nil, "filter coalesce")
62+
property["Optional obeys the Applicative identity law"] = forAll { (x : OptionalOf<Int>) in
63+
return (pure(identity) <*> x.getOptional) == x.getOptional
64+
}
7165

72-
}
66+
property["Optional obeys the first Applicative composition law"] = forAll { (fl : OptionalOf<ArrowOf<Int8, Int8>>, gl : OptionalOf<ArrowOf<Int8, Int8>>, x : OptionalOf<Int8>) in
67+
let f = fl.getOptional.map({ $0.getArrow })
68+
let g = gl.getOptional.map({ $0.getArrow })
69+
70+
let l = (curry() <^> f <*> g <*> x.getOptional)
71+
let r = (f <*> (g <*> x.getOptional))
72+
return l == r
73+
}
74+
75+
property["Optional obeys the second Applicative composition law"] = forAll { (fl : OptionalOf<ArrowOf<Int8, Int8>>, gl : OptionalOf<ArrowOf<Int8, Int8>>, x : OptionalOf<Int8>) in
76+
let f = fl.getOptional.map({ $0.getArrow })
77+
let g = gl.getOptional.map({ $0.getArrow })
7378

79+
let l = (pure(curry()) <*> f <*> g <*> x.getOptional)
80+
let r = (f <*> (g <*> x.getOptional))
81+
return l == r
82+
}
83+
84+
property["Optional obeys the Monad left identity law"] = forAll { (a : Int, fa : ArrowOf<Int, OptionalOf<Int>>) in
85+
let f = { $0.getOptional } fa.getArrow
86+
return (pure(a) >>- f) == f(a)
87+
}
88+
89+
property["Optional obeys the Monad right identity law"] = forAll { (m : OptionalOf<Int>) in
90+
return (m.getOptional >>- pure) == m.getOptional
91+
}
92+
93+
property["Optional obeys the Monad associativity law"] = forAll { (fa : ArrowOf<Int, OptionalOf<Int>>, ga : ArrowOf<Int, OptionalOf<Int>>, m : OptionalOf<Int>) in
94+
let f = { $0.getOptional } fa.getArrow
95+
let g = { $0.getOptional } ga.getArrow
96+
return ((m.getOptional >>- f) >>- g) == (m.getOptional >>- { x in f(x) >>- g })
97+
}
98+
}
7499
}

SwiftzTests/StringExtSpec.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,26 @@ class StringExtSpec : XCTestCase {
4545
return (x.fmap(identity)) == identity(x)
4646
}
4747

48-
reportProperty["String obeys the Functor composition law"] = forAll { (f : ArrowOf<Character, Character>, g : ArrowOf<Character, Character>, x : String) in
49-
return ((f.getArrow g.getArrow) <^> x) == (x.fmap(f.getArrow).fmap(g.getArrow))
48+
property["String obeys the Functor composition law"] = forAll { (f : ArrowOf<Character, Character>, g : ArrowOf<Character, Character>, x : String) in
49+
return ((f.getArrow g.getArrow) <^> x) == (x.fmap(g.getArrow).fmap(f.getArrow))
5050
}
5151

5252
property["String obeys the Applicative identity law"] = forAll { (x : String) in
5353
return (pure(identity) <*> x) == x
5454
}
5555

56-
reportProperty["String obeys the first Applicative composition law"] = forAll { (fl : ArrayOf<ArrowOf<Character, Character>>, gl : ArrayOf<ArrowOf<Character, Character>>, x : String) in
57-
let f = fl.getArray.map({ $0.getArrow })
58-
let g = gl.getArray.map({ $0.getArrow })
59-
return (curry() <^> f <*> g <*> x) == (f <*> (g <*> x))
60-
}
61-
62-
reportProperty["String obeys the second Applicative composition law"] = forAll { (fl : ArrayOf<ArrowOf<Character, Character>>, gl : ArrayOf<ArrowOf<Character, Character>>, x : String) in
63-
let f = fl.getArray.map({ $0.getArrow })
64-
let g = gl.getArray.map({ $0.getArrow })
65-
return (pure(curry()) <*> f <*> g <*> x) == (f <*> (g <*> x))
66-
}
56+
// Swift unroller can't handle our scale.
57+
// property["String obeys the first Applicative composition law"] = forAll { (fl : ArrayOf<ArrowOf<Character, Character>>, gl : ArrayOf<ArrowOf<Character, Character>>, x : String) in
58+
// let f = fl.getArray.map({ $0.getArrow })
59+
// let g = gl.getArray.map({ $0.getArrow })
60+
// return (curry(•) <^> f <*> g <*> x) == (f <*> (g <*> x))
61+
// }
62+
//
63+
// property["String obeys the second Applicative composition law"] = forAll { (fl : ArrayOf<ArrowOf<Character, Character>>, gl : ArrayOf<ArrowOf<Character, Character>>, x : String) in
64+
// let f = fl.getArray.map({ $0.getArrow })
65+
// let g = gl.getArray.map({ $0.getArrow })
66+
// return (pure(curry(•)) <*> f <*> g <*> x) == (f <*> (g <*> x))
67+
// }
6768

6869
property["String obeys the Monoidal left identity law"] = forAll { (x : String) in
6970
return (x + String.mzero) == x
@@ -90,7 +91,7 @@ class StringExtSpec : XCTestCase {
9091
return (xs >>- fs) == Array(xs).map(fs).reduce("", combine: +)
9192
}
9293

93-
reportProperty["filter behaves"] = forAll { (xs : String, pred : ArrowOf<Character, Bool>) in
94+
property["filter behaves"] = forAll { (xs : String, pred : ArrowOf<Character, Bool>) in
9495
return xs.filter(pred.getArrow).reduce({ $0.0 && pred.getArrow($0.1) }, initial: true)
9596
}
9697

0 commit comments

Comments
 (0)