LoginSignup
8
9

More than 5 years have passed since last update.

Swift3で継続モナドを書いてみる

Posted at

Swift3で継続モナドを書いてみました。

contMonad.swift

import Foundation

typealias CPSCallBack<R> = (R)->Void
typealias CPSFunction<R> = (@escaping CPSCallBack<R>)->Void


class ContMonad<R> {

    let cps: CPSFunction<R>

    init(cps:@escaping CPSFunction<R>) {
        self.cps = cps
    }

    func run(callBack:@escaping CPSCallBack<R>)->Void {
        return cps(callBack)
    }

    func fmap<T>(_ f:@escaping (R)->T) -> ContMonad<T>{
        return self >>= {(r:R) in unit(f(r))}
    }
}

func >>=<R,T>(left:ContMonad<R>, right:@escaping (R)->ContMonad<T>) -> ContMonad<T> {


    let f = {(cb:@escaping CPSCallBack<T>) -> Void in
         left.run(callBack:{r in
            let c:ContMonad<T> = right(r)
            c.run(callBack:{t in
                cb(t)
            })
        })
    }

    return ContMonad<T>(cps:f)
}

func unit<R>(_ x:R) -> ContMonad<R> {

    let cps:(CPSCallBack<R>)->Void = {(cb:CPSCallBack<R>)->Void in
        return cb(x)
    }

    return ContMonad<R>(cps:cps)
}


func fmap<R,T>(f:@escaping (R) -> T, m:ContMonad<R>) -> ContMonad<T> {

    return m >>= {(r:R) in unit(f(r))}
}

func fmap<R,T>(f:@escaping (R)->T) -> (ContMonad<R>) -> ContMonad<T> {
    return {m in fmap(f:f, m:m)}
}


enum CPSResult<X>{
    case just(X)
    case error(Error)
}

使用例として、IDを非同期で取得する関数、IDから非同期でレコードを取得する関数、結果の変換をする関数を定義し、最後に>>=つまりバインドでつなげます。

example.swift


func getRecordCont(uid:SomeUID?) -> ContMonad<CPSResult<SomeRecord>> {
    return ContMonad{cb in
        let cb_ = cb
        DispatchQueue.global().async {
            let r = SomeRecord(title: "\(uid?.uid) - record")
            cb_(CPSResult.just(r))
        }
    }
}

func getUIDsCont()->ContMonad<CPSResult<SomeUID>> {
    return ContMonad { cb in
        DispatchQueue.global().async {
            let r = SomeUID(uid: "PO")
            cb(CPSResult.just(r))
        }
    }
}

func uidFromResult(_ r:CPSResult<SomeUID>) -> SomeUID? {
    switch r {
    case let .just(uid):
        return uid
    default:
        return nil
    }
}


var b = getUIDsCont().fmap(uidFromResult)
let c = b >>= getRecordCont

c.run { r in
    print("ContMonad result = \(r)")
}

sleep(100)

まとめたものはこちらに
https://github.com/KatagiriSo/RDMonad/blob/master/RDMonad

8
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9