Skip to content

Instantly share code, notes, and snippets.

@Goos
Last active July 10, 2018 18:21
Show Gist options
  • Save Goos/bcd19adafa7baa331d7665c447c7d63e to your computer and use it in GitHub Desktop.
Save Goos/bcd19adafa7baa331d7665c447c7d63e to your computer and use it in GitHub Desktop.
Functional playground
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