Last active
December 14, 2019 16:38
-
-
Save luizmb/cb9d103a45a579059832d18b5c0aa98f to your computer and use it in GitHub Desktop.
Using Reader as Applicative vs Monad, in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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