Skip to content

Commit 2998fea

Browse files
committed
Merge pull request #208 from CodaFi/resulting-trust
Test Result
2 parents 4863fa9 + bc43e72 commit 2998fea

File tree

5 files changed

+155
-50
lines changed

5 files changed

+155
-50
lines changed

Swiftz.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@
147147
84DF75CF1B0BD1D400C912B0 /* TupleExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A8900D1A71DFC5003D53CF /* TupleExt.swift */; };
148148
84DF76301B0BDCE800C912B0 /* Swiftz.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DF762F1B0BDCE800C912B0 /* Swiftz.h */; settings = {ATTRIBUTES = (Public, ); }; };
149149
84DF76311B0BDCE800C912B0 /* Swiftz.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DF762F1B0BDCE800C912B0 /* Swiftz.h */; settings = {ATTRIBUTES = (Public, ); }; };
150+
84EA2C271B1CE70B0001FB3F /* ResultExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84EA2C261B1CE70B0001FB3F /* ResultExt.swift */; };
151+
84EA2C281B1CE70B0001FB3F /* ResultExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84EA2C261B1CE70B0001FB3F /* ResultExt.swift */; };
150152
84F2C4FA1A7AEB9B00316E5F /* JSONKeypath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2C4F91A7AEB9B00316E5F /* JSONKeypath.swift */; };
151153
/* End PBXBuildFile section */
152154

@@ -308,6 +310,7 @@
308310
84DF75281B0BD17700C912B0 /* Swiftz-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Swiftz-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
309311
84DF762F1B0BDCE800C912B0 /* Swiftz.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Swiftz.h; path = Swiftz/Swiftz.h; sourceTree = "<group>"; };
310312
84DF76321B0BDD4B00C912B0 /* SwiftCheck.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SwiftCheck.xcodeproj; path = Carthage/Checkouts/SwiftCheck/SwiftCheck.xcodeproj; sourceTree = SOURCE_ROOT; };
313+
84EA2C261B1CE70B0001FB3F /* ResultExt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultExt.swift; sourceTree = "<group>"; };
311314
84F2C4F91A7AEB9B00316E5F /* JSONKeypath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONKeypath.swift; sourceTree = "<group>"; };
312315
/* End PBXFileReference section */
313316

@@ -451,6 +454,7 @@
451454
84A88FE61A71DFC5003D53CF /* CharacterExt.swift */,
452455
84A88FEA1A71DFC5003D53CF /* DictionaryExt.swift */,
453456
84A890031A71DFC5003D53CF /* OptionalExt.swift */,
457+
84EA2C261B1CE70B0001FB3F /* ResultExt.swift */,
454458
84A890091A71DFC5003D53CF /* StringExt.swift */,
455459
84A8900D1A71DFC5003D53CF /* TupleExt.swift */,
456460
);
@@ -750,6 +754,7 @@
750754
84A890161A71DFC5003D53CF /* CharacterExt.swift in Sources */,
751755
8480AB4E1A7B448A00C6162D /* Sections.swift in Sources */,
752756
84A8902B1A71DFC5003D53CF /* Monad.swift in Sources */,
757+
84EA2C271B1CE70B0001FB3F /* ResultExt.swift in Sources */,
753758
84A890221A71DFC5003D53CF /* IxCont.swift in Sources */,
754759
84A890371A71DFC5003D53CF /* Semigroup.swift in Sources */,
755760
84A890331A71DFC5003D53CF /* OptionalExt.swift in Sources */,
@@ -831,6 +836,7 @@
831836
84DF759C1B0BD1D400C912B0 /* Kinds.swift in Sources */,
832837
84DF759D1B0BD1D400C912B0 /* Operators.swift in Sources */,
833838
84DF759E1B0BD1D400C912B0 /* Array.swift in Sources */,
839+
84EA2C281B1CE70B0001FB3F /* ResultExt.swift in Sources */,
834840
84DF759F1B0BD1D400C912B0 /* Box.swift in Sources */,
835841
84DF75A01B0BD1D400C912B0 /* Either.swift in Sources */,
836842
84DF75A11B0BD1D400C912B0 /* Error.swift in Sources */,

Swiftz/Applicative.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ public protocol Applicative : Pointed, Functor {
1515
/// Type of Functors containing morphisms from our objects to a target.
1616
typealias FAB = K1<A -> B>
1717

18-
/// Applies the function encapsulated by the Functor to the encapsulated by the receiver.
18+
/// Applies the function encapsulated by the Functor to the value encapsulated by the receiver.
1919
func ap(f : FAB) -> FB
2020
}

Swiftz/Pointed.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ extension Box : Pointed {
1919
}
2020

2121
extension Result : Pointed {
22-
typealias A = V
23-
2422
public static func pure(x : V) -> Result<V> {
2523
return Result.value(x)
2624
}

Swiftz/ResultExt.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// ResultExt.swift
3+
// Swiftz
4+
//
5+
// Created by Robert Widmann on 6/1/15.
6+
// Copyright (c) 2015 TypeLift. All rights reserved.
7+
//
8+
9+
// YAY, SEGFAULTS AGAIN.
10+
//
11+
//extension Result : Functor {
12+
// typealias A = V
13+
// typealias B = Any
14+
// typealias FB = Result<B>
15+
//
16+
// /// Applies the function to the value in the receiver.
17+
// ///
18+
// /// If the receiver is `.Error`, ignores the function and returns the receiver unmodified. If
19+
// /// the receiver is `.Value`, applies the function to the underlying value and returns the
20+
// /// result in a new `.Value`.
21+
// public func fmap<B>(f : V -> B) -> Result<B> {
22+
// return f <^> self
23+
// }
24+
//}
25+
//
26+
//extension Result : Applicative {
27+
// typealias FAB = Result<A -> B>
28+
//
29+
// /// Given an `Result<VA -> VB>`, applies the encapsulated function to the value in the receiver
30+
// /// if both are present.
31+
// ///
32+
// /// If the `f` or `a' param is an `.Error`, simply returns an `.Error` with the same value.
33+
// /// Otherwise the function taken from `.Value(f)` is applied to the value from `.Value(a)` and a
34+
// /// new `.Value` is returned.
35+
// public func ap<B>(f : Result<V -> B>) -> Result<B> {
36+
// return f <*> self
37+
// }
38+
//}
39+
//
40+
//extension Result : Monad {
41+
// /// Given a function from `VA -> Result<VB>`, applies the function `f` if `the receiver is a
42+
// /// `.Value`, otherwise the function is ignored and a `.Error` with the error value from the
43+
// /// receiver is returned.
44+
// public func bind<B>(f : A -> Result<B>) -> Result<B> {
45+
// return self >>- f
46+
// }
47+
//}

SwiftzTests/ResultSpec.swift

Lines changed: 101 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,112 @@
66
// Copyright (c) 2015 TypeLift. All rights reserved.
77
//
88

9-
import XCTest
109
import Swiftz
10+
import XCTest
11+
import SwiftCheck
12+
13+
let defaultError = NSError(domain: "Swiftz", code: -1, userInfo: nil)
14+
15+
struct ResultOf<A : Arbitrary> : Arbitrary, Printable {
16+
let getResult : Result<A>
17+
18+
init(_ maybe : Result<A>) {
19+
self.getResult = maybe
20+
}
21+
22+
var description : String {
23+
return "\(self.getResult)"
24+
}
25+
26+
private static func create(opt : Result<A>) -> ResultOf<A> {
27+
return ResultOf(opt)
28+
}
29+
30+
static func arbitrary() -> Gen<ResultOf<A>> {
31+
return Gen.frequency([
32+
(1, Gen.pure(ResultOf(.Error(defaultError)))),
33+
(3, liftM({ ResultOf(pure($0)) })(m1: A.arbitrary()))
34+
])
35+
}
36+
37+
static func shrink(bl : ResultOf<A>) -> [ResultOf<A>] {
38+
switch bl.getResult {
39+
case .Error(_):
40+
return []
41+
case let .Value(v):
42+
return [ResultOf(.Error(defaultError))] + A.shrink(v.value).map({ ResultOf(pure($0)) })
43+
}
44+
}
45+
}
46+
47+
func == <T : protocol<Arbitrary, Equatable>>(lhs : ResultOf<T>, rhs : ResultOf<T>) -> Bool {
48+
return lhs.getResult == rhs.getResult
49+
}
50+
51+
func != <T : protocol<Arbitrary, Equatable>>(lhs : ResultOf<T>, rhs : ResultOf<T>) -> Bool {
52+
return !(lhs == rhs)
53+
}
1154

1255
class ResultSpec : XCTestCase {
13-
func testResult() {
14-
let divisionError = NSError(domain: "DivisionDomain", code: 1, userInfo: nil)
15-
16-
func divTwoEvenly(x: Int) -> Result<Int> {
17-
if x % 2 == 0 {
18-
return Result.error(divisionError)
19-
} else {
20-
return Result.value(x / 2)
21-
}
56+
func testProperties() {
57+
property["There is an isomorphism between Either<NSError, A> and Result<A>"] = forAll { (l : ResultOf<Int>) in
58+
return l.getResult == l.getResult.toEither().toResult(identity)
59+
}
60+
61+
property["Results of Equatable elements obey reflexivity"] = forAll { (l : ResultOf<Int>) in
62+
return l == l
63+
}
64+
65+
property["Results of Equatable elements obey symmetry"] = forAll { (x : ResultOf<Int>, y : ResultOf<Int>) in
66+
return (x == y) == (y == x)
67+
}
68+
69+
property["Results of Equatable elements obey transitivity"] = forAll { (x : ResultOf<Int>, y : ResultOf<Int>, z : ResultOf<Int>) in
70+
return (x == y) && (y == z) ==> (x == z)
71+
}
72+
73+
property["Results of Equatable elements obey negation"] = forAll { (x : ResultOf<Int>, y : ResultOf<Int>) in
74+
return (x != y) == !(x == y)
75+
}
76+
77+
property["Result obeys the Functor identity law"] = forAll { (x : ResultOf<Int>) in
78+
return (identity <^> x.getResult) == identity(x.getResult)
79+
}
80+
81+
property["Result obeys the Functor composition law"] = forAll { (f : ArrowOf<Int, Int>, g : ArrowOf<Int, Int>, x : ResultOf<Int>) in
82+
return ((f.getArrow g.getArrow) <^> x.getResult) == (f.getArrow <^> (g.getArrow <^> x.getResult))
83+
}
84+
85+
property["Result obeys the Applicative identity law"] = forAll { (x : ResultOf<Int>) in
86+
return (pure(identity) <*> x.getResult) == x.getResult
87+
}
88+
89+
property["Result obeys the first Applicative composition law"] = forAll { (fl : ResultOf<ArrowOf<Int8, Int8>>, gl : ResultOf<ArrowOf<Int8, Int8>>, x : ResultOf<Int8>) in
90+
let f = { $0.getArrow } <^> fl.getResult
91+
let g = { $0.getArrow } <^> gl.getResult
92+
return (curry() <^> f <*> g <*> x.getResult) == (f <*> (g <*> x.getResult))
93+
}
94+
95+
property["Result obeys the second Applicative composition law"] = forAll { (fl : ResultOf<ArrowOf<Int8, Int8>>, gl : ResultOf<ArrowOf<Int8, Int8>>, x : ResultOf<Int8>) in
96+
let f = { $0.getArrow } <^> fl.getResult
97+
let g = { $0.getArrow } <^> gl.getResult
98+
return (pure(curry()) <*> f <*> g <*> x.getResult) == (f <*> (g <*> x.getResult))
99+
}
100+
101+
property["Result obeys the Monad left identity law"] = forAll { (a : Int, fa : ArrowOf<Int, ResultOf<Int>>) in
102+
let f = { $0.getResult } fa.getArrow
103+
return (pure(a) >>- f) == f(a)
104+
}
105+
106+
property["Result obeys the Monad right identity law"] = forAll { (m : ResultOf<Int>) in
107+
return (m.getResult >>- pure) == m.getResult
22108
}
23109

24-
// fold
25-
XCTAssert(Result.error(divisionError).fold(0, f: identity) == 0)
26-
XCTAssert(Result.value(10).fold(0, f: identity) == 10)
27-
28-
let start = 17
29-
let first: Result<Int> = divTwoEvenly(start)
30-
let prettyPrinted: Result<String> = { $0.description } <^> first
31-
let snd = first >>- divTwoEvenly
32-
XCTAssert(prettyPrinted == Result.value("8"))
33-
XCTAssert(snd == .Error(divisionError))
34-
35-
let startResult: Result<Int> = pure(start)
36-
XCTAssert(startResult == Result.value(17))
37-
let doubleResult: Result<Int -> Int> = pure({$0 * 2})
38-
XCTAssert((doubleResult <*> startResult) == Result.value(34), "test ap: (result, result)")
39-
let noF: Result<Int -> Int> = .Error(divisionError)
40-
let noX: Result<Int> = snd
41-
XCTAssert((noF <*> startResult) == .Error(divisionError), "test ap: (error, result)")
42-
XCTAssert((doubleResult <*> noX) == .Error(divisionError), "test ap: (result, error)")
43-
XCTAssert((noF <*> noX) == .Error(divisionError), "test ap: (error, error)")
44-
45-
// special constructor
46-
XCTAssert(Result(divisionError, 1) == .Error(divisionError), "special Result cons error")
47-
XCTAssert(Result(nil, 1) == Result.value(1), "special Result cons value")
110+
property["Result obeys the Monad associativity law"] = forAll { (fa : ArrowOf<Int, ResultOf<Int>>, ga : ArrowOf<Int, ResultOf<Int>>, m : ResultOf<Int>) in
111+
let f = { $0.getResult } fa.getArrow
112+
let g = { $0.getResult } ga.getArrow
113+
return ((m.getResult >>- f) >>- g) == (m.getResult >>- { x in f(x) >>- g })
114+
}
48115
}
49116

50117
func testResultFrom() {
@@ -62,17 +129,4 @@ class ResultSpec : XCTestCase {
62129
XCTAssertTrue((throwableFunction !! -1) == Result.error(e), "")
63130
XCTAssertTrue((throwableFunction !! 1) == Result.value("1"), "")
64131
}
65-
66-
func testEitherResult() {
67-
// tests:
68-
// - either -> result
69-
// - result -> either
70-
71-
let resultOne: Result<Int> = Result.value(1)
72-
let eitherOne: Either<NSError, Int> = resultOne.toEither()
73-
let resultAgain: Result<Int> = eitherOne.toResult(identity)
74-
XCTAssert(resultOne == resultAgain)
75-
76-
let typeinfworkplz = Result.Value(Box(1)).toEither().toResult(identity)
77-
}
78132
}

0 commit comments

Comments
 (0)