Last active
May 4, 2018 08:29
-
-
Save mansbernhardt/a410b3de02e618cb5c6a76cbb069be56 to your computer and use it in GitHub Desktop.
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 Foundation | |
import PlaygroundSupport | |
enum Result<Value> { | |
case success(Value) | |
case failure(Error) | |
} | |
func data(at url: URL, completion: @escaping (Result<Data>) -> Void) { | |
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { | |
print("done") | |
completion(.success(Data())) | |
} | |
} | |
struct Jar { | |
let json: Data | |
init(json: Data) throws { | |
self.json = json | |
} | |
} | |
extension Result { | |
init(getValue: () throws -> Value) { | |
do { | |
self = .success(try getValue()) | |
} catch { | |
self = .failure(error) | |
} | |
} | |
func getValue() throws -> Value { | |
switch self { | |
case .success(let value): return value | |
case .failure(let error): throw error | |
} | |
} | |
} | |
extension Result { | |
func map<O>(_ transform: (Value) throws -> O) -> Result<O> { | |
return Result<O> { try transform(getValue()) } | |
} | |
} | |
func json(at url: URL, completion: @escaping (Result<Jar>) -> Void) { | |
data(at: url) { result in | |
completion(result.map { try Jar(json: $0) }) | |
} | |
} | |
#if false // First implentation attempt | |
struct Future<Value> { | |
let onResult: (@escaping (Result<Value>) -> Void) -> Void | |
} | |
#elseif false // Second implentation attempt | |
final class Future<Value> { | |
var completions: [(Result<Value>) -> Void] = [] | |
init(onResult: @escaping (@escaping (Result<Value>) -> Void) -> Void) { | |
onResult { result in | |
self.completions.forEach { $0(result) } | |
} | |
} | |
func onResult(_ completion: @escaping (Result<Value>) -> Void) { | |
completions.append(completion) | |
} | |
} | |
Future<Int> { c in c(.success(1)) }.onResult { result in | |
// Will never be called as `c` was called before `onResult` was called. | |
} | |
#else // Third an final implentation attempt | |
final class Future<Value> { | |
enum State { | |
case completed(Result<Value>) | |
case pending([(Result<Value>) -> Void]) | |
} | |
var state: State | |
init(onResult: @escaping (@escaping (Result<Value>) -> Void) -> Void) { | |
state = .pending([]) | |
onResult { result in | |
guard case .pending(let completions) = self.state else { return } | |
completions.forEach { $0(result) } | |
self.state = .completed(result) | |
} | |
} | |
func onResult(_ completion: @escaping (Result<Value>) -> Void) { | |
switch self.state { | |
case .completed(let result): | |
completion(result) | |
case .pending(var completions): | |
completions.append(completion) | |
self.state = .pending(completions) | |
} | |
} | |
} | |
#endif | |
func data(at url: URL) -> Future<Data> { | |
return Future { completion in | |
data(at: url, completion: completion) | |
} | |
} | |
extension Future { | |
func map<O>(_ transform: @escaping (Value) throws -> O) -> Future<O> { | |
return Future<O> { completion in | |
self.onResult { result in | |
completion(result.map(transform)) | |
} | |
} | |
} | |
} | |
func json(at url: URL) -> Future<Jar> { | |
return data(at: url).map { try Jar(json: $0) } | |
} | |
extension Future { | |
func flatMap<O>(_ transform: @escaping (Value) throws -> Future<O>) -> Future<O> { | |
return Future<O> { completion in | |
self.onResult { result in | |
do { | |
try transform(result.getValue()).onResult(completion) | |
} catch { | |
completion(.failure(error)) | |
} | |
} | |
} | |
} | |
} | |
extension Future { | |
func onValue(_ callback: @escaping (Value) throws -> Void) -> Future { | |
return map { value in | |
try callback(value) | |
return value | |
} | |
} | |
} | |
print("Start request") | |
let url = URL(string: "https://www.izettle.com")! | |
json(at: url).flatMap { jar in | |
json(at: url) | |
}.map { jar in | |
jar | |
}.onValue { friends in | |
print("End Request") | |
}.onResult { _ in | |
// onResult needed by basic impl. | |
} | |
PlaygroundPage.current.needsIndefiniteExecution = true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment