Skip to content

Instantly share code, notes, and snippets.

@akbashev
Last active April 24, 2022 21:07
Show Gist options
  • Save akbashev/398f9c4e94f35c80744d77df944cccbe to your computer and use it in GitHub Desktop.
Save akbashev/398f9c4e94f35c80744d77df944cccbe to your computer and use it in GitHub Desktop.
Experimenting with Swift async await and wrapping it into IO and Async structures.
import Foundation
import UIKit // needed for Task in Playgrounds 🤷‍♂️🤔
public protocol Functor {
associatedtype A
associatedtype B: Functor = Self
func map<C>(_ f: @escaping (A) -> C) -> B where B.A == C
}
protocol Monad: Functor {
static func pure(_ value: Self.A) -> Self
func flatMap(_ f: @escaping (Self.A) -> Self) -> Self
}
struct IO<A> {
let run: () -> A
init(_ run: @escaping () -> A) {
self.run = run
}
init(_ value: A) {
self.run = { value }
}
}
// https://github.com/pointfreeco/swift-prelude/blob/0ec0a56bb99911647d2081f99a6f6c9699a6a646/Sources/Prelude/IO.swift#L28
extension IO {
public init(_ callback: @escaping (@escaping (A) -> ()) -> ()) {
self.init {
var computed: A?
let semaphore = DispatchSemaphore(value: 0)
callback {
computed = $0
semaphore.signal()
}
semaphore.wait()
return computed!
}
}
}
extension IO: Monad {
static func pure(_ value: A) -> IO<A> {
IO<A> { value }
}
func map<B>(_ f: @escaping (A) -> B) -> IO<B> {
IO<B> { f(run()) }
}
func flatMap<B>(_ f: @escaping (A) -> IO<B>) -> IO<B> {
IO<B>(f(run()).run)
}
}
struct Async<A> {
private let run: () async -> A
init(_ run: @escaping () async -> A) {
self.run = run
}
func run(_ callback: @escaping (A) -> ()) {
Task { await callback(self.run()) }
}
}
extension Async: Monad {
static func pure(_ value: A) -> Async<A> {
Async<A> { value }
}
func map<B>(_ f: @escaping (A) -> B) -> Async<B> {
Async<B> { f(await run()) }
}
func flatMap<B>(_ f: @escaping (A) -> Async<B>) -> Async<B> {
Async<B> { await f(await self.run()).run() }
}
}
extension Async {
func wait() -> IO<A> {
IO(self.run(_:))
}
}
// https://api.thecatapi.com/v1/images/search
struct Cat: Decodable {
let url: String
}
let decoder = JSONDecoder()
let get: (String) -> Async<String> = { url in
.init {
let (data, _) = try! await URLSession.shared.data(from: URL(string: url)!)
return try! decoder.decode([Cat].self, from: data).map { $0.url }.first!
}
}
let printStr: (String) -> IO<()> = { text in
IO { print(text) }
}
func main(_ io: @escaping () -> IO<()>) {
io().run()
}
main {
get("https://api.thecatapi.com/v1/images/search")
.map { "This is url: \($0)" }
.wait()
.flatMap { printStr($0) }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment