Skip to content

Commit ab94cf4

Browse files
committed
Merge pull request #15 from typelift/dev-ops
Explore Ops Typeclasses
2 parents 4c8f24b + 541298a commit ab94cf4

File tree

14 files changed

+551
-79
lines changed

14 files changed

+551
-79
lines changed

Basis/Applicative.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,23 @@ public protocol Alternative : Applicative {
6060
///
6161
/// The least solution to the equation:
6262
///
63-
/// some(v) <|> FLA.pure([])
63+
/// some(v) <|> pure([])
6464
func many(Self) -> FLA
6565
}
6666

67+
/// Additional functions to be implemented by those types conforming to the Applicative protocol.
68+
public protocol ApplicativeOps : Applicative {
69+
typealias C
70+
typealias FC = K1<C>
71+
typealias D
72+
typealias FD = K1<D>
73+
74+
/// Lift a function to a Functorial action.
75+
class func liftA(f : A -> B) -> Self -> FB
76+
77+
/// Lift a binary function to a Functorial action.
78+
class func liftA2(f : A -> B -> C) -> Self -> FB -> FC
79+
80+
/// Lift a ternary function to a Functorial action.
81+
class func liftA3(f : A -> B -> C -> D) -> Self -> FB -> FC -> FD
82+
}

Basis/Either.swift

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,7 @@ public func !=<A : Equatable, B : Equatable>(lhs: Either<A, B>, rhs: Either<A, B
134134
extension Either : Functor {
135135
typealias A = R
136136
typealias B = Any
137-
typealias C = Any
138-
typealias FB = Either<L, C>
137+
typealias FB = Either<L, B>
139138

140139
public static func fmap<C>(f : R -> C) -> Either<L, R> -> Either<L, C> {
141140
return {
@@ -150,7 +149,7 @@ extension Either : Functor {
150149

151150
}
152151

153-
public func <%><A, B, C>(f: B -> C, either : Either<A, B>) -> Either<A, C> {
152+
public func <%><A, B, C>(f : B -> C, either : Either<A, B>) -> Either<A, C> {
154153
return Either.fmap(f)(either)
155154
}
156155

@@ -191,17 +190,37 @@ public func <*<A, B, C>(a : Either<A, B>, b : Either<A, C>) -> Either<A, B> {
191190
return const <%> a <*> b
192191
}
193192

193+
extension Either : ApplicativeOps {
194+
typealias C = Any
195+
typealias FC = Either<L, C>
196+
typealias D = Any
197+
typealias FD = Either<L, D>
198+
199+
public static func liftA<B>(f : A -> B) -> Either<L, A> -> Either<L, B> {
200+
return { a in Either<L, A -> B>.pure(f) <*> a }
201+
}
202+
203+
public static func liftA2<B, C>(f : A -> B -> C) -> Either<L, A> -> Either<L, B> -> Either<L, C> {
204+
return { a in { b in f <%> a <*> b } }
205+
}
206+
207+
public static func liftA3<B, C, D>(f : A -> B -> C -> D) -> Either<L, A> -> Either<L, B> -> Either<L, C> -> Either<L, D> {
208+
return { a in { b in { c in f <%> a <*> b <*> c } } }
209+
}
210+
}
211+
194212
extension Either : Monad {
195-
public func bind<C>(f : R -> Either<L, C>) -> Either<L, C> {
213+
public func bind<B>(f : A -> Either<L, B>) -> Either<L, B> {
196214
switch self.destruct() {
197215
case .Left(let l):
198-
return Either<L, C>.left(l)
216+
return Either<L, B>.left(l)
199217
case .Right(let r):
200218
return f(r.unBox())
201219
}
202220
}
203221
}
204-
public func >>-<A, B, C>(xs : Either<A, B>, f : Either<A, B>.A -> Either<A, C>) -> Either<A, C> {
222+
223+
public func >>-<L, A, B>(xs : Either<L, A>, f : A -> Either<L, B>) -> Either<L, B> {
205224
return xs.bind(f)
206225
}
207226

@@ -211,6 +230,48 @@ public func >><A, B, C>(x : Either<A, B>, y : Either<A, C>) -> Either<A, C> {
211230
}
212231
}
213232

233+
extension Either : MonadOps {
234+
typealias MLA = Either<L, [A]>
235+
typealias MLB = Either<L, [B]>
236+
typealias MU = Either<L, ()>
237+
238+
public static func mapM<B>(f : A -> Either<L, B>) -> [A] -> Either<L, [B]> {
239+
return { xs in Either<L, B>.sequence(map(f)(xs)) }
240+
}
241+
242+
public static func mapM_<B>(f : A -> Either<L, B>) -> [A] -> Either<L, ()> {
243+
return { xs in Either<L, B>.sequence_(map(f)(xs)) }
244+
}
245+
246+
public static func forM<B>(xs : [A]) -> (A -> Either<L, B>) -> Either<L, [B]> {
247+
return flip(Either.mapM)(xs)
248+
}
249+
250+
public static func forM_<B>(xs : [A]) -> (A -> Either<L, B>) -> Either<L, ()> {
251+
return flip(Either.mapM_)(xs)
252+
}
253+
254+
public static func sequence(ls : [Either<L, A>]) -> Either<L, [A]> {
255+
return foldr({ m, m2 in m >>- { x in m2 >>- { xs in Either<L, [A]>.pure(cons(x)(xs)) } } })(Either<L, [A]>.pure([]))(ls)
256+
}
257+
258+
public static func sequence_(ls : [Either<L, A>]) -> Either<L, ()> {
259+
return foldr(>>)(Either<L, ()>.pure(()))(ls)
260+
}
261+
}
262+
263+
public func -<<<L, A, B>(f : A -> Either<L, B>, xs : Either<L, A>) -> Either<L, B> {
264+
return xs.bind(f)
265+
}
266+
267+
public func >-><L, A, B, C>(f : A -> Either<L, B>, g : B -> Either<L, C>) -> A -> Either<L, C> {
268+
return { x in f(x) >>- g }
269+
}
270+
271+
public func <-<<L, A, B, C>(g : B -> Either<L, C>, f : A -> Either<L, B>) -> A -> Either<L, C> {
272+
return { x in f(x) >>- g }
273+
}
274+
214275
extension Either : MonadFix {
215276
public static func mfix(f : R -> Either<L, R>) -> Either<L, R> {
216277
func fromRight(e : Either<L, R>) -> R {

Basis/IO.swift

Lines changed: 79 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,37 @@ public struct IO<A> {
2525
}
2626
}
2727

28+
/// Wraps up a closure in a lazy IO action.
29+
///
30+
/// Do-notation is a special syntax taken from Haskell. In Haskell, one uses do-notation as a
31+
/// shorthand for chains of Monadic operators, and for more "imperative-looking" code. While Swift
32+
/// does not allow us to define a DSL that specific, we can use the implicit sequencing of its
33+
/// imperative statements and call execution of those statements our "desugaring".
34+
///
35+
/// Generally, do-notation in the Basis is used around side-effecting code. Do-blocks in Swift are
36+
/// therefore more useful as a means of delimiting pure and side-effecting code from one another,
37+
/// while also retaining the benefits of the lazy IO monad.
38+
///
39+
/// It is important to note that IO actions returned are lazy. This means the provided block will
40+
/// not be executed until the values inside are requested, either with an extract (`<-`) or a call
41+
/// to `unsafePerformIO()`
42+
public func do_<A>(fn: () -> IO<A>) -> IO<A> {
43+
return IO<A>({ rw in (rw, !fn()) })
44+
}
45+
46+
/// Wraps up a closure returning a value in a lazy IO action.
47+
///
48+
/// This variant of do-blocks allows one to write more natural looking code. The return keyword
49+
/// becomes monadic return.
50+
public func do_<A>(fn: () -> A) -> IO<A> {
51+
return IO<A>({ rw in (rw, fn()) })
52+
}
53+
54+
/// An more convenient form of unsafePerformIO.
55+
public prefix func !<A>(m: IO<A>) -> A {
56+
return m.unsafePerformIO()
57+
}
58+
2859
/// Writes a character to standard output.
2960
public func putChar(c : Character) -> IO<Void> {
3061
return IO.pure(print(c))
@@ -126,6 +157,25 @@ public func <* <A, B>(a : IO<A>, b : IO<B>) -> IO<A> {
126157
return const <%> a <*> b
127158
}
128159

160+
extension IO : ApplicativeOps {
161+
typealias C = Any
162+
typealias FC = IO<C>
163+
typealias D = Any
164+
typealias FD = IO<D>
165+
166+
public static func liftA<B>(f : A -> B) -> IO<A> -> IO<B> {
167+
return { a in IO<A -> B>.pure(f) <*> a }
168+
}
169+
170+
public static func liftA2<B, C>(f : A -> B -> C) -> IO<A> -> IO<B> -> IO<C> {
171+
return { a in { b in f <%> a <*> b } }
172+
}
173+
174+
public static func liftA3<B, C, D>(f : A -> B -> C -> D) -> IO<A> -> IO<B> -> IO<C> -> IO<D> {
175+
return { a in { b in { c in f <%> a <*> b <*> c } } }
176+
}
177+
}
178+
129179
extension IO : Monad {
130180
public func bind<B>(f: A -> IO<B>) -> IO<B> {
131181
return IO<B>({ rw in
@@ -145,73 +195,46 @@ public func >><A, B>(x: IO<A>, y: IO<B>) -> IO<B> {
145195
})
146196
}
147197

148-
public prefix func !<A>(m: IO<A>) -> A {
149-
return m.unsafePerformIO()
150-
}
198+
extension IO : MonadOps {
199+
typealias MLA = IO<[A]>
200+
typealias MLB = IO<[B]>
201+
typealias MU = IO<()>
151202

152-
/// Wraps up a closure in a lazy IO action.
153-
///
154-
/// Do-notation is a special syntax taken from Haskell. In Haskell, one uses do-notation as a
155-
/// shorthand for chains of Monadic operators, and for more "imperative-looking" code. While Swift
156-
/// does not allow us to define a DSL that specific, we can use the implicit sequencing of its
157-
/// imperative statements and call execution of those statements our "desugaring".
158-
///
159-
/// Generally, do-notation in the Basis is used around side-effecting code. Do-blocks in Swift are
160-
/// therefore more useful as a means of delimiting pure and side-effecting code from one another,
161-
/// while also retaining the benefits of the lazy IO monad.
162-
///
163-
/// It is important to note that IO actions returned are lazy. This means the provided block will
164-
/// not be executed until the values inside are requested, either with an extract (`<-`) or a call
165-
/// to `unsafePerformIO()`
166-
public func do_<A>(fn: () -> IO<A>) -> IO<A> {
167-
return IO<A>({ rw in (rw, !fn()) })
168-
}
203+
public static func mapM<B>(f : A -> IO<B>) -> [A] -> IO<[B]> {
204+
return { xs in IO<B>.sequence(map(f)(xs)) }
205+
}
169206

170-
/// Wraps up a closure returning a value in a lazy IO action.
171-
///
172-
/// This variant of do-blocks allows one to write more natural looking code. The return keyword
173-
/// becomes monadic return.
174-
public func do_<A>(fn: () -> A) -> IO<A> {
175-
return IO<A>({ rw in (rw, fn()) })
176-
}
207+
public static func mapM_<B>(f : A -> IO<B>) -> [A] -> IO<()> {
208+
return { xs in IO<B>.sequence_(map(f)(xs)) }
209+
}
177210

178-
/// Executes a list of IO actions sequentially, accumulating their results in a list in another IO
179-
/// action
180-
public func sequence<A>(ms : [IO<A>]) -> IO<[A]> {
181-
return foldr({ m in { n in
182-
do_ { () -> [A] in
183-
let x : A = !m
184-
let xs : [A] = !n
185-
return [x] + xs
186-
}
187-
}})(IO.pure([]))(ms)
188-
}
211+
public static func forM<B>(xs : [A]) -> (A -> IO<B>) -> IO<[B]> {
212+
return flip(IO.mapM)(xs)
213+
}
189214

190-
/// Executes a list of IO actions sequentially, discarding the result of each along the way.
191-
public func sequence_<A>(ms : [IO<A>]) -> IO<Void> {
192-
return foldr({ x, y in x.bind({ _ in y }) })(IO<Void>.pure(Void()))(ms)
193-
}
215+
public static func forM_<B>(xs : [A]) -> (A -> IO<B>) -> IO<()> {
216+
return flip(IO.mapM_)(xs)
217+
}
218+
219+
public static func sequence(ls : [IO<A>]) -> IO<[A]> {
220+
return foldr({ m, m2 in m >>- { x in m2 >>- { xs in IO<[A]>.pure(cons(x)(xs)) } } })(IO<[A]>.pure([]))(ls)
221+
}
194222

195-
/// Maps a function over a list, then sequences the resulting IO actions together, accumulating
196-
/// their results in a list in another IO action.
197-
public func mapM<A, B>(f : A -> IO<B>) -> [A] -> IO<[B]> {
198-
return { ms in sequence(ms.map(f)) }
223+
public static func sequence_(ls : [IO<A>]) -> IO<()> {
224+
return foldr(>>)(IO<()>.pure(()))(ls)
225+
}
199226
}
200227

201-
/// Maps a function over a list, then sequences the resulting IO actions together, discarding the
202-
/// result of each along the way.
203-
public func mapM_<A, B>(f : A -> IO<B>) -> [A] -> IO<Void> {
204-
return { ms in sequence_(ms.map(f)) }
228+
public func -<<<A, B>(f : A -> IO<B>, xs : IO<A>) -> IO<B> {
229+
return xs.bind(f)
205230
}
206231

207-
/// mapM with its arguments flipped around.
208-
public func forM<A, B>(l: [A])(f : A -> IO<B>) -> IO<[B]> {
209-
return mapM(f)(l)
232+
public func >-><A, B, C>(f : A -> IO<B>, g : B -> IO<C>) -> A -> IO<C> {
233+
return { x in f(x) >>- g }
210234
}
211235

212-
/// mapM_ with its arguments flipped around.
213-
public func forM_<A, B>(l: [A])(f : A -> IO<B>) -> IO<Void> {
214-
return mapM_(f)(l)
236+
public func <-<<A, B, C>(g : B -> IO<C>, f : A -> IO<B>) -> A -> IO<C> {
237+
return { x in f(x) >>- g }
215238
}
216239

217240
extension IO : MonadFix {

Basis/Lazy.swift

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ extension Lazy : Applicative {
8888
}
8989
}
9090

91-
9291
public func <*><A, B>(stfn: Lazy<A -> B>, st: Lazy<A>) -> Lazy<B> {
9392
return Lazy<A>.ap(stfn)(st)
9493
}
@@ -101,6 +100,25 @@ public func <*<A, B>(a : Lazy<A>, b : Lazy<B>) -> Lazy<A> {
101100
return const <%> a <*> b
102101
}
103102

103+
extension Lazy : ApplicativeOps {
104+
typealias C = Any
105+
typealias FC = Lazy<C>
106+
typealias D = Any
107+
typealias FD = Lazy<D>
108+
109+
public static func liftA<B>(f : A -> B) -> Lazy<A> -> Lazy<B> {
110+
return { a in Lazy<A -> B>.pure(f) <*> a }
111+
}
112+
113+
public static func liftA2<B, C>(f : A -> B -> C) -> Lazy<A> -> Lazy<B> -> Lazy<C> {
114+
return { a in { b in f <%> a <*> b } }
115+
}
116+
117+
public static func liftA3<B, C, D>(f : A -> B -> C -> D) -> Lazy<A> -> Lazy<B> -> Lazy<C> -> Lazy<D> {
118+
return { a in { b in { c in f <%> a <*> b <*> c } } }
119+
}
120+
}
121+
104122
extension Lazy : Monad {
105123
public func bind<B>(f: A -> Lazy<B>) -> Lazy<B> {
106124
return f(force(self))
@@ -116,3 +134,45 @@ public func >><A, B>(x : Lazy<A>, y : Lazy<B>) -> Lazy<B> {
116134
return y
117135
})
118136
}
137+
138+
extension Lazy : MonadOps {
139+
typealias MLA = Lazy<[A]>
140+
typealias MLB = Lazy<[B]>
141+
typealias MU = Lazy<()>
142+
143+
public static func mapM<B>(f : A -> Lazy<B>) -> [A] -> Lazy<[B]> {
144+
return { xs in Lazy<B>.sequence(map(f)(xs)) }
145+
}
146+
147+
public static func mapM_<B>(f : A -> Lazy<B>) -> [A] -> Lazy<()> {
148+
return { xs in Lazy<B>.sequence_(map(f)(xs)) }
149+
}
150+
151+
public static func forM<B>(xs : [A]) -> (A -> Lazy<B>) -> Lazy<[B]> {
152+
return flip(Lazy.mapM)(xs)
153+
}
154+
155+
public static func forM_<B>(xs : [A]) -> (A -> Lazy<B>) -> Lazy<()> {
156+
return flip(Lazy.mapM_)(xs)
157+
}
158+
159+
public static func sequence(ls : [Lazy<A>]) -> Lazy<[A]> {
160+
return foldr({ m, m2 in m >>- { x in m2 >>- { xs in Lazy<[A]>.pure(cons(x)(xs)) } } })(Lazy<[A]>.pure([]))(ls)
161+
}
162+
163+
public static func sequence_(ls : [Lazy<A>]) -> Lazy<()> {
164+
return foldr(>>)(Lazy<()>.pure(()))(ls)
165+
}
166+
}
167+
168+
public func -<<<A, B>(f : A -> Lazy<B>, xs : Lazy<A>) -> Lazy<B> {
169+
return xs.bind(f)
170+
}
171+
172+
public func >-><A, B, C>(f : A -> Lazy<B>, g : B -> Lazy<C>) -> A -> Lazy<C> {
173+
return { x in f(x) >>- g }
174+
}
175+
176+
public func <-<<A, B, C>(g : B -> Lazy<C>, f : A -> Lazy<B>) -> A -> Lazy<C> {
177+
return { x in f(x) >>- g }
178+
}

0 commit comments

Comments
 (0)