LoginSignup
8
5

More than 3 years have passed since last update.

Swift5.1のFunction Builderで契約プログラミングっぽい記述をする

Posted at

Swift5.1で導入されるFunction Builderを学ぶため、Function Builderで契約プログラミング(DbC)っぽいことをできるようにしてみます。(実用性は無いですが)

まずIn, Out, Bodyを構成するための型を用意します。

public struct In<S> {
    var message: String
    var contract: (S) -> Bool

    public init(_ message: String = "", _ contract: @escaping (S) -> Bool) {
        self.message = message
        self.contract = contract
    }
}

public struct Out<T> {
    var message: String
    var contract: (T) -> Bool

    public init(_ message: String = "", _ contract: @escaping (T) -> Bool) {
        self.message = message
        self.contract = contract
    }
}

public struct Body<S, T> {
    var body: (S) -> T

    public init(body: @escaping (S) -> T) {
        self.body = body
    }
}

次にFunction Bulider用の型を記述します。

@_functionBuilder public struct DbCBuilder {
    static func buildBlock<S, T>(_ a: In<S>, _ b: Body<S, T>) -> (S) -> T {
        return { s in
            precondition(a.contract(s), a.message)
            return b.body(s)
       }
    }

    static func buildBlock<S, T>(_ a: Out<T>, _ b: Body<S, T>) -> (S) -> T {
        return { s in
            let t = b.body(s)
            precondition(a.contract(t), a.message)
            return t
        }
    }

    static func buildBlock<S, T>(_ a: In<S>, _ b: Out<T>, _ c: Body<S, T>) -> (S) -> T {
        return { s in
            precondition(a.contract(s), a.message)
            let t = c.body(s)
            precondition(b.contract(t), b.message)
            return t
        }
    }
}

buildBlockを3つ用意しました。In+Body, Out+Body, In+Out+Bodyです。

このFunction Builderを利用する関数を記述します。

func dbc<S, T>(@DbCBuilder block: () -> (S) -> T) -> (S) -> T {
    return block()
}

これで準備ができました。
これらを利用した関数を記述します。

func calc(_ i: Int) -> Int {
    dbc {
        In { $0 > 1 }
        Body { $0 * $0 }
    }(i)
}

func add(_ a: Int, _ b: Int) -> Int {
    dbc {
        In { $0 > 0 && $1 > 0 }
        Out { $0 > 2 }
        Body { $0 + $1 }
    }((a, b))
}

calc(2)
add(1, 2)

dbc関数の中でIn+BodyやIn+Out+Bodyを返すことでDbCBuilderの各buildBlockが実行され(S)->Tが返され、dbc関数の返値となります。

8
5
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
5