コンパイル時オペレーション
Kazuki Moriyama (森山 和樹)
dottyでは型に対してコンパイル時に行える操作が色々増えている。
特にリテラル型に当たるものは値レベルの操作と遜色無いものが用意されている。
コンパイル時オペレーションの中でもscala.compiletime.ops以下に当たるパッケージを見ていこう。
https://github.com/lampepfl/dotty/tree/master/library/src/scala/compiletime/ops
Intの操作
intリテラルに対して操作を行うことができ、演算があらかた定義されていてびっくりする。
例えば足し算。
import scala.compiletime.ops.int.*
val two: 1 + 1 = 2
summon[2 =:= 1 + 1]
これらはすべてコンパイルが通る。
もちろん計算結果がおかしければコンパイルは通らない。
summon[3 =:= 1 + 1]
// [error] 7 | summon[3 =:= 1 + 1]
// [error] | ^
// [error] | Cannot prove that (3 : Int) =:= (2 : Int).
他にも四則演算、シフト演算、大小比較、絶対値など色々。
変わり種ではMinやMaxなどが定義されている。
summon[2 =:= Max[1, 2]]
summon[1 =:= Min[1, 2]]
あとは型レベルでStringに変換したり。
summon["1" =:= ToString[1]]
何でもありすぎてビビる。
あとはSが面白い。
これは今まで型レベルで自然数を定義するときに使われていたある自然数の次の数を求める型である。
summon[1 =:= S[0]]
Sは自然数に対してのみ定義されている。
summon[0 =:= S[-1]]
// [error] 12 | summon[0 =:= S[-1]]
// [error] | ^
// [error] | Cannot prove that (0 : Int) =:= compiletime.ops.int.S[(-1 : Int)].
定義自体は非常に簡潔なので一度自分で見てみるとおすすめする。
https://github.com/lampepfl/dotty/blob/master/library/src/scala/compiletime/ops/int.scala
Stringの操作
Intの操作に比べてできることは少なく、足し算のみ。
import scala.compiletime.ops.string.*
summon["hello taro" =:= "hello " + "taro"]
Booleanの操作
否定、排他的論理和、論理和、論理積。
import scala.compiletime.ops.boolean.*
summon[true =:= ![false]]
summon[false =:= ![true]]
summon[false =:= (true ^ true)]
summon[true =:= (false ^ true)]
summon[true =:= (true && true)]
summon[false =:= (true && false)]
summon[true =:= (true || false)]
summon[false =:= (false || false)]
どんな型に対しても行える操作
型が同じかどうかを判定する操作が用意されている。
今まではgeneralized type constraint(=:=、<:<)でインスタンスが見つかれば〜という感じだったが、これはBooleanのサブ型を返してくる。
import scala.compiletime.ops.int.*
import scala.compiletime.ops.any.*
summon[true =:= (2 == S[1])]
summon[false =:= (2 == S[2])]