Slide 1

Slide 1 text

Go1.20から追加されたtree構造のerrの紹介と、 treeを考慮した複数マッチができる ライブラリを作った話 2023/06/02(金) Go Conference 2023 Online

Slide 2

Slide 2 text

自己紹介 @convto 株式会社LayerX所属 レイヤ低めの技術などに興味がありま す (ちなみにidの読みはこんぶとです)

Slide 3

Slide 3 text

contents - tree構造のerrの紹介 - proposalの議論などを おさらい - ありうるより高度なマッ チ要求 - マッチ処理に関する実 験 - 実験の考察 - まとめ

Slide 4

Slide 4 text

contents - tree構造のerrの紹介 - proposalの議論などを おさらい - ありうるより高度なマッ チ要求 - マッチ処理に関する実 験 - 実験の考察 - まとめ

Slide 5

Slide 5 text

Go1.20で複数errをwrapできるようになった https://tip.golang.org/doc/go1.20#errors

Slide 6

Slide 6 text

どういうことができるようになるか - こういうのができます - errをつくるときに複数errを合成できるように なり、マッチ処理でもそのような errが考慮さ れるようになる - あたらしいエラー構造とハンドリングが標準 パッケージでサポートされる!

Slide 7

Slide 7 text

いままでとなにがちがうか いままではこう - wrapされたerrの関係性は連結リスト的 - 枝分かれなし errA errB errC errD

Slide 8

Slide 8 text

いままではこう - wrapされたerrの関係性は連結リスト的 - 枝分かれなし これからはこう - errorをたどると枝分かれしてる可能性があ る - tree構造 errA errB errC errD errA errB errC errD errC errD errD いままでとなにがちがうか

Slide 9

Slide 9 text

contents - tree構造のerrの紹介 - proposalの議論などを おさらい - ありうるより高度なマッ チ要求 - マッチ処理に関する実 験 - 実験の考察 - まとめ

Slide 10

Slide 10 text

proposal

Slide 11

Slide 11 text

どういうproposal? - 複数errを一つのerrにしたい - `errors.Join()` や `fmt.Errorf()` で複数エラーの結合が可能に - 結合されたerrは `Unwrap() []error` を実装していて取り出し可能 - 当初は `Split(error) []error` も提案されていた(後の議論でスコープ外に)

Slide 12

Slide 12 text

Is/Asの探索はどのようなデザインか - treeを深さ優先探索する - マッチしたものがあればそこで探索を打ち切り結果を返す - tree上のすべての枝がIs/Asにマッチすることを保証しない - 当時存在したエコシステム上のmultierrライブラリの一般的な挙動などを参考にし て設定

Slide 13

Slide 13 text

Is/Asの探索はどのようなデザインか errA errB errC errD errD errD errC

Slide 14

Slide 14 text

Is/Asの探索はどのようなデザインか errA errB errC errD errC errD errD

Slide 15

Slide 15 text

Is/Asの探索はどのようなデザインか errA errB errC errD errC errD errD

Slide 16

Slide 16 text

Is/Asの探索はどのようなデザインか errA errB errC errD errC errD errD

Slide 17

Slide 17 text

Is/Asの探索はどのようなデザインか errA errB errC errD errC errD errD

Slide 18

Slide 18 text

探索はどのようなデザインか - そんなに複雑でないし、あまり特別なこともし ていない - 枝分かれする探索部分のイメージをみると わかりやすいかも

Slide 19

Slide 19 text

proposal中のデザインに関連する議論をピックアップ - SplitなりWalkなりの探索APIは検討しなくてよい? - IsとかAsの意味合いがすこし変わっちゃうけどよい?

Slide 20

Slide 20 text

SplitなりWalkなりの探索APIは検討しなくてよい?

Slide 21

Slide 21 text

もともとはproposalにsplitが含まれていたが... - 探索自体は需要あるかもだけど、デザインについて議論の余地があった - `Split()` or `Walk()` とか - まずはmultierrのコアとなる仕様だけstdに入れよう! - そうすればサードパーティで実験できるから、適切なデザインを探れる

Slide 22

Slide 22 text

IsとかAsの意味合いがすこし変わっちゃうけどよい?

Slide 23

Slide 23 text

どういうこと? - `Is()` はマッチした時点で探索をやめて true を返す - すべての分岐する枝で同一エラーかどうか を保証しない - ある枝はtrueでも別の枝でfalseなとき一部 のエラーハンドリングで困るかも

Slide 24

Slide 24 text

どういうこと? - `Is()` はマッチした時点で探索をやめて true を返す - すべての分岐する枝で同一エラーかどうか を保証しない - ある枝はtrueでも別の枝でfalseなとき一部 のエラーハンドリングで困るかも err crit err ignorable err こういうtreeだったとき cliterrを見逃してしまうかも

Slide 25

Slide 25 text

最終的には提案された仕様のままとなった - 一瞬 `Contains()` と `Is()` を区別したほうが良いかもみたいなこと言ってる人はい た - 既存multierr実装に合わせた形で仕様が提案されているので `Is()` が最初のマッ チで探索を打ち切る挙動は一般的に期待されるものと一致してるだろうという結論

Slide 26

Slide 26 text

proposalふりかえりまとめ - tree構造を考慮したIs/Asが標準に追加 - 探索APIなどは今のところ未提供で、要実験 - マッチ処理も一部議論があったが提案通りの仕様で受け入れられた

Slide 27

Slide 27 text

contents - tree構造のerrの紹介 - proposalの議論などを おさらい - ありうるより高度なマッ チ要求 - マッチ処理に関する実 験 - 実験の考察 - まとめ

Slide 28

Slide 28 text

デザインのおさらい - treeを深さ優先探索する - マッチしたものがあればそこで探索を打ち切り結果を返す - tree上のすべての枝がIs/Asにマッチすることを保証しない

Slide 29

Slide 29 text

考えられる問題は? - As/Isで意味合いが変わるので一部コードに問題がでるかも(ほぼ影響なし) - 構造が複雑になったので、文脈によってより高度なマッチ処理がしたくなるかも

Slide 30

Slide 30 text

考えられる問題は? - As/Isで意味合いが変わるので一部コードに問題がでるかも(ほぼ影響なし) - 構造が複雑になったので、文脈によってより高度なマッチ処理がしたくなるかも

Slide 31

Slide 31 text

考えられる問題は? - As/Isで意味合いが変わるので一部コードに問題がでるかも(ほぼ影響なし) - 構造が複雑になったので、文脈によってより高度なマッチ処理がしたくなるかも -> いまのAPIでうまく探索できるのかが課題

Slide 32

Slide 32 text

例1: より正確なIs()

Slide 33

Slide 33 text

errors.Is(errA, errD) errA errB errC errD errC errD errD

Slide 34

Slide 34 text

errors.Is(errA, errD) errA errB errC errD errC errD errD これがhitした時点で探索やめちゃう

Slide 35

Slide 35 text

errors.Is(errA, errD) errA errB errC errD errC errD errD これがhitした時点で探索やめちゃう このへんの枝がhitしてなくても true返す

Slide 36

Slide 36 text

errors.Is(errA, errD) errA errB errD errD errD errE errE こういうときだけtrueかえしてほしい

Slide 37

Slide 37 text

例2: 全件抽出するAs()

Slide 38

Slide 38 text

errors.As(errA, errD) errA errB errC errD errC errD errD これがhitした時点でerrDを返す

Slide 39

Slide 39 text

errors.As(errA, errD) errA errB errC errD errC errD errD これがhitした時点でerrDを返す このへんも返してほしくない?

Slide 40

Slide 40 text

errors.As(errA, errD) errA errB errC errD errC errD errD こういう結果を取り出したい

Slide 41

Slide 41 text

気になったので検証してみた - 例で上げた2つのマッチ処理を実装する - tree上のすべての枝に該当エラーが存在するか確認したい(例1) - tree上の該当エラーを枝などの関係性を問わずすべて抽出したい(例2) - いまの仕様で実装できるか試してみる

Slide 42

Slide 42 text

contents - tree構造のerrの紹介 - proposalの議論などを おさらい - ありうるより高度なマッ チ要求 - マッチ処理に関する実 験 - 実験の考察 - まとめ

Slide 43

Slide 43 text

実験: より正確なIs()

Slide 44

Slide 44 text

errors.Is(errA, errD) errA errB errD errD errD errE errE こういうときだけtrueかえしてほしい

Slide 45

Slide 45 text

errors.Is(errA, errD) errA errB errC errD errD errE errE これはfalse

Slide 46

Slide 46 text

そもそも標準のIsってどう探索してるんだっけ? - `Unwrap() error` と `Unwrap() []error` をア サーションして探索 - 分岐してなければ剥がして終端でなければ loop - 分岐してれば各枝に再帰的に Is

Slide 47

Slide 47 text

枝が別れたら全部trueならtrueとすればよし - 分岐したときにマッチしない枝があれば false - ちなみに `Unwrap() []error` が len 0 となる ケースはいまの標準パッケージの実装では ないはず - いちおうproposalで空のケースが言及されて るのでみている

Slide 48

Slide 48 text

実験: 全件抽出するAs()

Slide 49

Slide 49 text

errors.As(errA, errD) errA errB errC errD errC errD errD こういう結果を取り出したい

Slide 50

Slide 50 text

どのようなものを作るか - Asのように具体的な型で取り出したい - `Scan[T any](err error, target T) (matched []T)` のようなデザインで考える - targetの具体的な型でmatchedを返せる - ただの抽出処理でありboolは返さないデザイン

Slide 51

Slide 51 text

Scan実装 - assignable or `As` 実装済みでマッチしたら 結果に追加 - 枝が分岐してなければ剥がして終端でなけ ればloop - 枝が分岐してれば再帰的に Scan

Slide 52

Slide 52 text

contents - tree構造のerrの紹介 - proposalの議論などを おさらい - ありうるより高度なマッ チ要求 - マッチ処理に関する実 験 - 実験の考察 - まとめ

Slide 53

Slide 53 text

ようはtreeへのマッチ処理で、さまざまな要求がありうる - tree上に該当エラーが1つ以上存在するか確認したい(go1.20のstd Isで提供され ているもの) - tree上に該当エラーが1つ以上存在するか確認して取り出したい(go1.20のstd As で提供されているもの) - tree上のすべての枝に該当エラーが存在するか確認したい(例1) - tree上の該当エラーを枝などの関係性を問わずすべて抽出したい(例2)

Slide 54

Slide 54 text

ようはtreeへのマッチ処理で、さまざまな要求がありうる - tree上に該当エラーが1つ以上存在するか確認したい(go1.20のstd Isで提供され ているもの) - tree上に該当エラーが1つ以上存在するか確認して取り出したい(go1.20のstd As で提供されているもの) - tree上のすべての枝に該当エラーが存在するか確認したい(例1) - tree上の該当エラーを枝などの関係性を問わずすべて抽出したい(例2) - こういうのもあるかも - 例1と例2を組み合わせて「マッチ判定は tree上のすべての要素が対象、 hitしたものはすべて抽出」 - etc..

Slide 55

Slide 55 text

実験前はこう思っていたけど... - 探索可能APIはほしいかも - joinしたerrをバラけさせるSplit()とか - errをバラして深さ優先探索して次の errを返すWalk()とか - 枝分かれする部分とそうじゃない部分を透過的に扱えると嬉しそう?

Slide 56

Slide 56 text

要求によっては連結リストと分岐部分を区別したいかも - 透過的な `Walk()` しちゃうと困るケースがあるかも - tree構造のerrは最近入ったばかりなので、エコシステム全体で実験を重ねていけ ばよりよい形が見えてくるのかも - そう考えるといまの `Upwrap() error` or `Unwrap() []error` で素朴に型判定する 方針はよいバランス感覚かも?

Slide 57

Slide 57 text

contents - tree構造のerrの紹介 - proposalの議論などを おさらい - ありうるより高度なマッ チ要求 - マッチ処理に関する実 験 - 実験の考察 - まとめ

Slide 58

Slide 58 text

まとめ - 標準パッケージにtree構造のエラーへのサポートが入った - これによりエラーハンドリングにあたらしい複雑さが生まれた - 標準パッケージでは共通してみんなが使いそうなマッチ処理がサポートされている - 高度なマッチ処理がやりたかったら探索できるAPIがある - エコシステム全体で実験を重ねて知見をためればよりよい探索APIが見つかるかも - Goの簡素なエラー処理が個人的には好きなので、その性質を維持しつつよい方向 に整理されていくとよいですね!

Slide 59

Slide 59 text

ご清聴ありがとうございました