https://qiita.com/sxarp/items/cd528a546d1537105b9d
直和型を実装するのではなく、(型の)パターンマッチの方を実装します。 型のパターンマッチはクロージャーを使って以下のように表現できます。
IntOrString.Match(Cases{ // 型のパターンマッチ
Int: func(i Int) { sum += int(i) }, // Int型だった場合の処理
String: func(s String) { sum += len(string(s)) }, // String型だった場合の処理
})
Union型/直和型とはどちらか片方の型の値を取るような型のことで典型的には以下のような構文で表現されます。
この機能を持っている言語としては静的型付け関数型言語(Haskell/OCaml/F#)、強力な型機能のある言語(Rust/TypeScript)などがあります。
Goを使っていても、"複数の型を取りうる型"を考えたくなることがあり、この場合よくやられている方法として次のようなものがあると思います。
以下のようなvalue.(type)
を使った書き方はよく見られます。
switch v := value.(type) {
case string:
sum += len(v)
case int:
sum += v
}
ここでの問題はvalue
をinterface{}
として扱う必要があり、型による保証を受けられず型安全ではありません。
型を要素として含むようなstruct
を作るやり方もよく見られます。
この方法の問題点は不整合な値が許容されることです。 具体的には複数のフィールドに非ゼロな値が入ってしまうことが可能で、こうなってしまうともやは直和型の値とは呼べません。
v := IntStringUnion{
Int: 10,
String: "hoge",
}
また、元となる型のzero-valueをどう表現するのかという問題もあります。
直和型を利用する際には、パターンマッチを使って元の型を取り出して処理しますが、このような処理を実装することを考えます。
具体的には以下のようなパターンマッチを表現するメソッドMatch
を持った型(インターフェイス)を考えます。