普段仕事ではTypeScriptを書いているので
const x: "kinoko" | "takenoko" = "kinoko";
みたいなの(Union型という)に見慣れていた。
趣味ではScalaを書いており、同じようなものを書きたくなった。具体的にはActiviryPubのActor Typesを表現するためにApplication, Group, Organization, Person, Serviceのどれかの値(いずれも文字列)をとるという型を作りたかった。
case class
しかし調べてみるとScalaにUnion型はない。自分でパッと思いついたのはcase classだ。ドワンゴのScalaテキストに近い用例が載っている。下にそれを引用する。
sealed abstract class DayOfWeek
case object Sunday extends DayOfWeek
case object Monday extends DayOfWeek
case object Tuesday extends DayOfWeek
case object Wednesday extends DayOfWeek
case object Thursday extends DayOfWeek
case object Friday extends DayOfWeek
case object Saturday extends DayOfWeek
sealed
修飾子をスーパークラス/トレイトに付けることによって、その(直接の)サブクラス/トレイトは同じファイル内にしか定義できない
ので、パターンマッチするときにコンパイラがいい感じにチェックしてくれるというメリットがある。
上記を参考にこんな感じで実装してみた。欲しいのは文字列なので、toStringをoverrideしてクラス名をそのまま取得できるようにした。
objectにするとgetClass.getNameの末尾に$がついてしまうのでinitしている。最後の1文字を除去するというメソッドがある多機能さ、いかにもScalaっぽい。あるいはcase objectじゃなくてcase classにすればこんな小細工は不要だが、パラメータリストが必要になるという面倒臭さがある。どうあるべきなのか、自信ニキは教えてほしい。
Enum
もう一つ、これは同僚に教えてもらったものだが、Enum型を使う方法もある。
こちらは採用しなかったのであまり深入りしていない。もしかしたらこっちのほうが良かったかもしれない。いや、いま猛烈にそんな気がしている(表現したいのが単なる文字列なので)。
それにしても大事なのはググり力だ。僕が「Scala ユニオン型」で検索してもいい情報は得られなかったが、「代数的データ型」という言葉を知っていればこの記事に辿り着けただろう。思うに、体系的な勉強は裏切らないのだ。
型の道は深く険しい…