Skip to content

Instantly share code, notes, and snippets.

@luizmb
Last active December 14, 2019 16:38
Show Gist options
  • Save luizmb/cb9d103a45a579059832d18b5c0aa98f to your computer and use it in GitHub Desktop.
Save luizmb/cb9d103a45a579059832d18b5c0aa98f to your computer and use it in GitHub Desktop.
Using Reader as Applicative vs Monad, in Swift
import Combine
import Foundation
import PlaygroundSupport
struct Reader<E, A> {
let run: (E) -> A
func map<B>(_ fn: @escaping (A) -> B) -> Reader<E, B> {
Reader<E, B> { e in
fn(self.run(e))
}
}
func flatMap<B>(_ fn: @escaping (A) -> Reader<E, B>) -> Reader<E, B> {
Reader<E, B> { e in
fn(self.run(e)).run(e)
}
}
func contramap<World>(_ fn: @escaping (World) -> E) -> Reader<World, A> {
Reader<World, A> { world in
self.run(fn(world))
}
}
}
func zip<A, B, C, E>(_ fa: Reader<E, A>,
_ fb: Reader<E, B>,
with map: @escaping (A, B) -> C) -> Reader<E, C> {
Reader<E, C> { env in
let a = fa.run(env)
let b = fb.run(env)
return map(a, b)
}
}
struct Environment {
let date: () -> Date
}
let number: Reader<Environment, Deferred<Future<Int, Never>>> =
Reader { e in
Deferred {
Future<Int, Never>.init { completion in
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
completion(.success(42))
}
}
}
}
let stringifyApplicative: Reader<Environment, (Int) -> Deferred<Future<String, Never>>> =
Reader { e in
{ number in
Deferred {
Future<String, Never>.init { completion in
print("I'm pretending I care about environment at \(e.date())")
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
completion(.success(String(number) + "! @ \(e.date())"))
}
}
}
}
}
let stringifyMonad: (Int) -> Reader<Environment, Deferred<Future<String, Never>>> =
{ number in
Reader { e in
Deferred {
Future<String, Never>.init { completion in
print("I'm pretending I care about environment at \(e.date())")
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
completion(.success(String(number) + "! @ \(e.date())"))
}
}
}
}
}
let e = Environment(date: Date.init)
// Too much nesting, need to open Reader for composition :-(
func asMonad() -> Cancellable {
number.flatMap { numberPromise in
Reader { env in
numberPromise.flatMap { stringifyMonad($0).run(env) }
}
}
.run(e).sink { result in
Swift.print(result)
}
}
// Omg, I can use zip to simplify composition! Zip will operate on Reader, unwrapping everything
// at once and I can compose the content only once
func asApplicative() -> Cancellable {
zip(number, stringifyApplicative) { number, string in
number.flatMap(string)
}
.run(e).sink { result in
Swift.print(result)
}
}
let c0 = asMonad()
let c1 = asApplicative()
PlaygroundPage.current.needsIndefiniteExecution = true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment