プログラミングにおけるEnumeration、略してEnumは列挙型とも呼ばれ、いくつかあるうちの特定の値しか取らない型を表現するために使われる型である。Enumはだいたいの言語で様々な形で実装されている。
さて、Scala 2までではEnumを使う際はやや特殊なクラスを利用する必要があった。
これの出来はあまり良くなかったので、ユーザはこれを独自に実装するか、適当な数字にalias typeをつけてごまかしたり、専用のライブラリを使ったりしていた。
嬉しいことに、Scala 3ではEnumがファーストクラスで提供されている。
Enumはenumキーワードを使って定義でき、そのまま型に表れることができる。
enum Color {
case Red, Green, Blue
}
def complementary(c: Color): (Color, Color) = ...
complementary(Color.Red) // => (Green, Blue)
そして、toStringを使うことで、Enumから文字列表現を得ることも簡単にできる:
Color.Red.toString() // => "Red"
逆に、文字列からEnumを構築するにはvalueOfを使う:
Color.valueOf("Red") // => Color.Red
valueOfが型安全じゃない
さて、valueOfは型安全ではない。"Foo"などを渡すと実行時にコケてしまう。
そこで、型安全にvalueOfを使うにはscala.util.control.Exception.allCatchを使わなければならない・・・。
import scala.util.control.Exception.allCatch def colorify(s: String): Option[Color] = allCatch opt { Color.valueOf(s) } colorify("foo") // => None
(関係ないとのことだった)できれば、ビルトインで型安全にvalueOfという名前からして、おそらくLiteral Types関連の機能を呼び出しているのかもしれない。String => Option[enum]になるようなメソッドが生えていてほしい・・・。
文脈
なんでこんな機能が欲しいかというと、CLIパーサライブラリであるDeclineでEnumを使いたいためである。
Declineは賢いので、文字列から型Aに変換する方法を用意すれば、コマンドライン文字列からその型Aを復元して渡せるようになる。しかしながらScala 3のEnumにはまだ対応していないため、手でボイラープレートを書かなければならない。しかもenumのvalueOfは実行時例外を発するので、かなりダサい感じになってしまうな・・・という問題意識があるのであった。
追記
valuesというArrayでかえるmethodはあるので、それで
— Kenji Yoshida (@xuwei_k) 2023年5月19日
.values.find(_.toString == name) // Optionになる
とするか
scala.deriving.Mirror.SumOf
のメタプロ機能使って独自に作るか、じゃないですかね
"おそらくLiteral Types関連の機能"
は全然関係ないです
valuesを使うと配列がもらえるのでここから探すと良いとのことだった。