Last active
July 10, 2018 18:21
-
-
Save Goos/bcd19adafa7baa331d7665c447c7d63e to your computer and use it in GitHub Desktop.
Functional playground
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 PlaygroundSupport | |
PlaygroundPage.current.needsIndefiniteExecution = true | |
import UIKit | |
func bind<T, U, V>(_ first: (T) -> U, _ second: (U) -> V) -> (T) -> V { | |
return { (input: T) in | |
let res = first(input) | |
return second(res) | |
} | |
} | |
func bind<T, U>(_ first: @autoclosure(escaping) () -> T, _ second: (T) -> U) -> () -> U { | |
return { | |
let res = first() | |
return second(res) | |
} | |
} | |
func bind<T, U, V, Z>(_ first: Task<T, U, V>, _ second: (V) -> Z) -> Task<T, U, Z> { | |
let transformer = bind(first.transformer, second) | |
let task = Task<T, U, Z>(first.parameter, transformer) | |
return task | |
} | |
func f<T>(_ value: @autoclosure(escaping) () -> T) -> () -> T { | |
return value | |
} | |
infix operator >>= { associativity right precedence 255 } | |
func >>=<T, U, V>(_ first: (T) -> U, _ second: (U) -> V) -> (T) -> V { | |
return bind(first, second) | |
} | |
//func >>=<T, U>(_ first: @autoclosure(escaping) () -> T, _ second: (T) -> U) -> () -> U { | |
// return bind(first, second) | |
//} | |
func >>=<T, U, V, Z>(_ first: Task<T, U, V>, _ second: (V) -> Z) -> Task<T, U, Z> { | |
return bind(first, second) | |
} | |
func map<T, U>(_ transform: (T) -> U) -> ([T]) -> [U] { | |
return { (collection: [T]) in | |
return collection.map(transform) | |
} | |
} | |
func flatMap<T, U>(_ transform: (T) -> [U]) -> ([T]) -> [U] { | |
return { (collection: [T]) in | |
return collection.flatMap(transform) | |
} | |
} | |
func thing(_ input: Int) -> Int { | |
return input + 1 | |
} | |
func pick<T>(_ idx: Int) -> ([T]) -> T? { | |
return { $0.count > idx ? $0[idx] : nil } | |
} | |
final class Task<Parameter, InputType, OutputType> { | |
private(set) var parameter: Parameter | |
private var result: OutputType? = nil | |
private var transformer: (InputType) -> OutputType | |
private var handlers: [(OutputType) -> ()] | |
private var completed = false | |
class func plain<T, U>(_ parameter: T) -> Task<T, U, U> { | |
return Task<T, U, U>(parameter, { $0 }) | |
} | |
init(_ parameter: Parameter, _ transformer: (InputType) -> OutputType) { | |
self.parameter = parameter | |
self.transformer = transformer | |
self.handlers = [] | |
} | |
func complete(_ input: InputType) { | |
self.result = transformer(input) | |
handlers.forEach { $0(self.result!) } | |
completed = true | |
} | |
func ready(_ handler: (OutputType) -> ()) { | |
if completed { | |
handler(result!) | |
} else { | |
handlers.append(handler) | |
} | |
} | |
} | |
/* This isn't allowed due to the error "Same-type requirement makes | |
generic parameters equivalent". Yup, that's kind of what I want | |
Swift.. For the time being, the "plain" class method will have | |
to do. */ | |
//extension Task where InputType == OutputType { | |
// init(_ parameter: Parameter) { | |
// self.parameter = parameter | |
// self.transformer = { $0 } | |
// self.handlers = [] | |
// } | |
//} | |
typealias PlainTask<T, U> = Task<T, U, U> | |
final class HTTPClient { | |
typealias Input = URLRequest | |
typealias Output = Data? | |
func perform<T>(_ task: Task<URLRequest, Data?, T>, completion: (T) -> ()) { | |
NSURLConnection.sendAsynchronousRequest(task.parameter, queue: OperationQueue.main(), completionHandler: { (res, data, err) in | |
task.complete(data) | |
completion(task.result!) | |
}) | |
} | |
} | |
func request(_ urlString: String) -> PlainTask<URLRequest, Data?> { | |
return PlainTask<URLRequest, Data?>.plain(URLRequest(url: URL(string: urlString)!)) | |
} | |
func stringFromData(_ data: Data?) -> String? { | |
return data != nil ? String(data: data!, encoding: .ascii) : nil | |
} | |
func grep(_ pattern: String?) -> (String?) -> [String] { | |
return { string in | |
let expression = try! RegularExpression(pattern: pattern!, options: []) | |
let matches = expression.matches(in: string!, options: [], range: NSMakeRange(0, string!.characters.count - 1)) | |
return matches.map { (string as NSString?)!.substring(with: $0.range) } | |
} | |
} | |
let client = HTTPClient() | |
/* Creates a task. The task itself doesn't perform any side-effects, but | |
represents something that can be performed. */ | |
let req = request("http://google.se") | |
// Functors can be bound to the task, creating a new task | |
>>= stringFromData | |
>>= grep("div") | |
>>= flatMap { ["<\($0)>", "thing", "</\($0)>"] } | |
/* Another type is responsible for carrying out the task. This is | |
where side-effects live. Once done, the performer will call a | |
callback with the mapped value. */ | |
client.perform(req) { | |
print($0) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment