1
import Foundation
2

3
/// The Selective typeclass represents Selective Applicative Functors, described in [this paper](https://www.staff.ncl.ac.uk/andrey.mokhov/selective-functors.pdf). Selective Applicative Functors enrich Applicative Functors by providing an operation that composes two effectful computations where the second depends on the first.
4
public protocol Selective: Applicative {
5
    /// Conditionally applies the second computation based on the result of the first.
6
    ///
7
    /// - Parameters:
8
    ///   - fab: A computation that results in an `Either` value.
9
    ///   - f: A computation that is executed in case the first computation evaluates to a left value.
10
    /// - Returns: Composition of the two computations.
11
    static func select<A, B>(_ fab: Kind<Self, Either<A, B>>, _ f: Kind<Self, (A) -> B>) -> Kind<Self, B>
12
}
13

14
// MARK: Related functions
15

16
public extension Selective {
17 0
    private static func selector(_ x: Kind<Self, Bool>) -> Kind<Self, Either<(), ()>> {
18 0
        return map(x, { flag in flag ? Either.left(()) : Either.right(()) })
19
    }
20

21
    /// Evaluates the second computation when the first evaluates to `true`.
22
    ///
23
    /// - Parameters:
24
    ///   - cond: A computation evaluating to a boolean value.
25
    ///   - then: A computation that will be evaluated if the first computation evaluates to `true`.
26
    /// - Returns: Composition of the two computations.
27 0
    static func whenS(_ cond: Kind<Self, Bool>, then f: Kind<Self, ()>) -> Kind<Self, ()> {
28 0
        let effect = map(f) { ff in { (_: ()) in } }
29 0
        return select(selector(cond), effect)
30
    }
31

32
    /// Evaluates one out of two computations based on the result of another computation.
33
    ///
34
    /// - Parameters:
35
    ///   - fab: Computation producing an `Either` value, to decide which computation is executed afterwards.
36
    ///   - ifLeft: Computation that will be executed if `fab` evaluates to an `Either.left` value.
37
    ///   - ifRight: Computation that will be executed if `fab` evaluates to an `Either.right` value.
38
    /// - Returns: Composition of the computations.
39 0
    static func branch<A, B, C>(_ fab: Kind<Self, Either<A, B>>, ifLeft fa: Kind<Self, (A) -> C>, ifRight fb: Kind<Self, (B) -> C>) -> Kind<Self, C> {
40 0
        let x = map(fab) { eab in Either.fix(eab.map(Either<B, C>.left)) }
41 0
        let y = map(fa) { f in { a in Either<B, C>.right(f(a)) } }
42 0
        return select(select(x, y), fb)
43
    }
44

45
    /// Evaluates one out of two computations based on the result of another computation.
46
    ///
47
    /// - Parameters:
48
    ///   - x: Computation producing a boolean value to decide which computation is exectured afterwards.
49
    ///   - then: Computation that will be executed if the first evaluates to `true`.
50
    ///   - else: Computation that will be executed if the first evaluates to `false`.
51
    /// - Returns: Composition of the computations.
52 0
    static func ifS<A>(_ x: Kind<Self, Bool>, then t: Kind<Self, A>, else e: Kind<Self, A>) -> Kind<Self, A> {
53 0
        return branch(selector(x), ifLeft: map(t, { tt in constant(tt) }), ifRight: map(e, { ee in constant(ee) }))
54
    }
55

56
    /// A lifted version of lazy boolean or.
57
    ///
58
    /// - Parameters:
59
    ///   - x: Computation to be or'ed.
60
    ///   - y: Computation to be or'ed.
61
    /// - Returns: Result of the or operation on the two computations.
62 0
    static func orS(_ x: Kind<Self, Bool>, _ y: Kind<Self, Bool>) -> Kind<Self, Bool> {
63 0
        return ifS(x, then: pure(true), else: y)
64
    }
65

66
    /// A lifted version of lazy boolean and.
67
    ///
68
    /// - Parameters:
69
    ///   - x: Computation to be and'ed.
70
    ///   - y: Computation to be and'ed.
71
    /// - Returns: Result of the and operation on the two computations.
72 0
    static func andS(_ x: Kind<Self, Bool>, _ y: Kind<Self, Bool>) -> Kind<Self, Bool> {
73 0
        return ifS(x, then: y, else: pure(false))
74
    }
75

76
    /// Evaluates an optional computation, providing a default value for the empty case.
77
    ///
78
    /// - Parameters:
79
    ///   - x: Default value for the empty case.
80
    ///   - mx: A computation resulting in an optional value.
81
    /// - Returns: Composition of the two computations.
82 0
    static func fromOptionS<A>(_ x: Kind<Self, A>, _ mx: Kind<Self, Option<A>>) -> Kind<Self, A> {
83 0
        let s = map(mx) { a in Option.fix(a.map(Either<(), A>.right)).getOrElse(Either.left(())) }
84 0
        return select(s, map(x, { xx in constant(xx) }))
85
    }
86

87
    /// A lifted version of `any`. Retains the short-circuiting behavior.
88
    ///
89
    /// - Parameters:
90
    ///   - p: A lifted predicate to find any element of the `array` that matches it.
91
    ///   - array: An array to look for an element that matches the predicate in.
92
    /// - Returns: A boolean computation describing if any element of the array matches the predicate.
93 0
    static func anyS<A>(_ p: @escaping (A) -> Kind<Self, Bool>, _ array: ArrayK<A>) -> Kind<Self, Bool> {
94 0
        return array.foldRight(Eval.now(pure(false))) { a, b in Eval.later { orS(p(a), b.value()) } }.value()
95
    }
96

97
    /// A lifted version of `all`. Retains the short-circuiting behavior.
98
    ///
99
    /// - Parameters:
100
    ///   - p: A lifted predicate to check all elements of the `array` match it.
101
    ///   - array: An array to check if all elements match the predicate.
102
    /// - Returns: A boolean computation describing if all elements of the array match the predicate.
103 0
    static func allS<A>(_ p: @escaping (A) -> Kind<Self, Bool>, _ array: ArrayK<A>) -> Kind<Self, Bool> {
104 0
        return array.foldRight(Eval.now(pure(true))) { a, b in Eval.later { andS(p(a), b.value()) } }.value()
105
    }
106

107
    /// Evaluates a computation as long as it evaluates to `true`.
108
    ///
109
    /// - Parameter x: A computation.
110
    /// - Returns: A potentially lazy computation.
111 0
    static func whileS(_ x: Kind<Self, Bool>) -> Eval<Kind<Self, ()>> {
112 0
        return Eval.later { whenS(x, then: whileS(x).value()) }
113
    }
114
}
115

116
// MARK: Syntax for Selective
117

118
public extension Kind where F: Selective {
119
    /// Conditionally applies a computation based on the result of this computation.
120
    ///
121
    /// - Parameters:
122
    ///   - f: A computation that is executed in case the receiving computation evaluates to a left value.
123
    /// - Returns: Composition of the two computations.
124 0
    func select<AA, B>(_ f: Kind<F, (AA) -> B>) -> Kind<F, B> where A == Either<AA, B> {
125 0
        return F.select(self, f)
126
    }
127

128
    /// Evaluates one out of two computations based on the result of this computation.
129
    ///
130
    /// - Parameters:
131
    ///   - ifLeft: Computation that will be executed if this computation evaluates to an `Either.left` value.
132
    ///   - ifRight: Computation that will be executed if this computation evaluates to an `Either.right` value.
133
    /// - Returns: Composition of the computations.
134 0
    func branch<AA, B, C>(ifLeft fa: Kind<F, (AA) -> C>, ifRight fb: Kind<F, (B) -> C>) -> Kind<F, C> where A == Either<AA, B> {
135 0
        return F.branch(self, ifLeft: fa, ifRight: fb)
136
    }
137

138
    // Evaluates an optional computation, providing a default value for the empty case.
139
    ///
140
    /// - Parameters:
141
    ///   - defaultValue: Default value for the empty case.
142
    /// - Returns: Composition of the two computations.
143 0
    func fromOptionS<AA>(defaultValue x: Kind<F, AA>) -> Kind<F, AA> where A == Option<AA> {
144 0
        return F.fromOptionS(x, self)
145
    }
146
}
147

148
public extension Kind where F: Selective, A == Bool {
149
    /// Evaluates the second computation when this evaluates to `true`.
150
    ///
151
    /// - Parameters:
152
    ///   - then: A computation that will be evaluated if the first computation evaluates to `true`.
153
    /// - Returns: Composition of the two computations.
154 0
    func whenS(then f: Kind<F, ()>) -> Kind<F, ()> {
155 0
        return F.whenS(self, then: f)
156
    }
157

158
    /// Evaluates one out of two computations based on the result of another computation.
159
    ///
160
    /// - Parameters:
161
    ///   - then: Computation that will be executed if this evaluates to `true`.
162
    ///   - else: Computation that will be executed if this evaluates to `false`.
163
    /// - Returns: Composition of the computations.
164 0
    func ifS<A>(then t: Kind<F, A>, else e: Kind<F, A>) -> Kind<F, A> {
165 0
        return F.ifS(self, then: t, else: e)
166
    }
167

168
    /// A lifted version of lazy boolean or.
169
    ///
170
    /// - Parameters:
171
    ///   - y: Computation to be or'ed.
172
    /// - Returns: Result of the or operation on the two computations.
173 0
    func orS(_ y: Kind<F, Bool>) -> Kind<F, Bool> {
174 0
        return F.orS(self, y)
175
    }
176

177
    /// A lifted version of lazy boolean and.
178
    ///
179
    /// - Parameters:
180
    ///   - y: Computation to be and'ed.
181
    /// - Returns: Result of the and operation on the two computations.
182 0
    func andS(_ y: Kind<F, Bool>) -> Kind<F, Bool> {
183 0
        return F.andS(self, y)
184
    }
185

186
    /// Evaluates this computation as long as it evaluates to `true`.
187
    ///
188
    /// - Returns: A potentially lazy computation.
189 0
    func whileS() -> Eval<Kind<F, ()>> {
190 0
        return F.whileS(self)
191
    }
192
}
193

194
public extension ArrayK {
195
    /// A lifted version of `any`. Retains the short-circuiting behavior.
196
    ///
197
    /// - Parameters:
198
    ///   - p: A lifted predicate to find any element of this array that matches it.
199
    /// - Returns: A boolean computation describing if any element of the array matches the predicate.
200 0
    func anyS<F: Selective>(_ p: @escaping (A) -> Kind<F, Bool>) -> Kind<F, Bool> {
201 0
        return F.anyS(p, self)
202
    }
203

204
    /// A lifted version of `all`. Retains the short-circuiting behavior.
205
    ///
206
    /// - Parameters:
207
    ///   - p: A lifted predicate to check all elements of this array match it.
208
    /// - Returns: A boolean computation describing if all elements of the array match the predicate.
209 0
    func allS<F: Selective>(_ p: @escaping (A) -> Kind<F, Bool>) -> Kind<F, Bool> {
210 0
        return F.allS(p, self)
211
    }
212
}

Read our documentation on viewing source code .

Loading