2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

以前作った@resultBuilderでHTMLを書き出すDSLを手直ししながら、@resultBuilderの使い所について考える

Last updated at Posted at 2022-11-03

SwiftUIとともに颯爽と登場した@_functionBuilder(aka @resultBuilder)ですが、いまいち使い所を見出せないまま時は過ぎました。

自分の観測範囲に限れば、SwiftUI以外で@resultBuilderが有効に使われているライブラリは見当たらず、持て余してるのは自分だけじゃないと思って溜飲を下げていたところです。

そんな中、Swift 5.7が正規表現に対応し、その中で正規表現構築用のDSLを用意してきました。

RegexBuilder | Apple Developer Documentation

swift-evolution/0351-regex-builder.md at main · apple/swift-evolution

その手があったか!」と感心しました。

メールアドレスの正規表現 - Perl正規表現雑技

以前より、スクリプトで正規表現を構築する、という手法があることは認識していましたが、@resultBuilderで構造的に、Swiftの強力な型システムやXcodeのコード補完の恩恵を受けながら誤解のないように記述させる、というのは、なるほど説得力があります。


前置きが長くなりましたが、そんな事があり、今一度@resultBuilderに向き合ってみようと思い、以前習作として書いた、@resultBuilderでHTMLを書き出すDSLを見直してみることにしました。

Build HTML with ResultBuilder

割と長いので全部は掲載しませんが、上記GistのコードをSwift Playgroundにコピペすれば実行できるはずです。

何をやっているかは大体コメントに書いております。

このDSLのポイントは、@resultBuilder内のすべての要素の基盤となるElement内のrender()になります。

L4-

/// 要素
protocol Element {
    /// HTMLを書き出すレンダー
    /// - Returns: HTML
    func render() -> String
}

L47-

        // 内部タグ
        let i = innerHTML?.reduce(into: "") {
            $0 += $1.render()
        } ?? ""

render()内でネストされた要素(内部タグ)のrender()を呼び出すことにより、タグが連鎖的に書き出されることになります。

要素をネストさせる場合、DSL内部の全ての要素に共通する基盤のprotocolを用意し、連鎖的に実行させる仕組みは必要になってくると思われます。

また今回手直ししたのは、単なるテキストを表示させる部分で、以前はText()というstructを用意していたのですが、StringElementに適合させることによってだいぶスッキリ記述できるようにしました。

変更前

struct Text: Element {
    var innerText = ""

    init(_ innerText: String) {
        self.innerText = innerText
    }

    func render() -> String {
        innerText
    }
}
            P(["style": "color:red"]) {
                Text("テキスト") // String
                p
                1 // Int
            }

変更後

extension String: Element {
    func render() -> String {
        self
    }
}
            P(["style": "color:red"]) {
                "テキスト" // String
                p
                1 // Int
            }

その意味でも共通する基盤のprotocolの準備はあった方が良いと思われます。

ついでにIntにも対応しました。


手直ししていて、@resultBuilderは「記述を簡便にする」という使い方よりも、「ガチガチに制限を入れて曖昧な部分を無くす」という使い方の方が合っていそうです。

Xcodeのコード補完があれば、制限をガチガチにしても記述はそこまで手間にならないと思います。

現状タグの属性をDictionaryで指定するようにしていますが、ここをメソッドにするなどでさらに制限を加える事ができます。

HTML自体はそこまで厳密なものではないですが、「構造化されたデータを曖昧さがないように記述させる」というところが@resultBuilderの使い所の気がします。

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?