【Nim 小ネタ】2つの型が同一かどうかを判定する
この記事は「Nim Advent Calendar 2021」の17日目の記事として登録させてもらっています。
問題
突然ですが、genericsの型引数が同一の型を指すのかどうかを判定したい場合どうすればよいでしょうか。
proc hoge[T,U]() = when T == U: assert( false ) echo "なにかの処理" hoge[int,string]() # 正しいhoge使い方 hoge[int,int]() # 不正なhoge使い方。コンパイル時にエラーを出したい。
このhogeプロシージャ(generics)の型引数にはそれぞれ別の型を与えてほしいので、同じ型を渡された場合はコンパイル時にエラーを出すようにしたいとします。
上記コードではT == U
で型が同一かどうか判定しようとしていますが、そんな文法はないのでコンパイルエラーになってしまいます。
is演算子を使ってみる
Is演算子を使うことで型が同一かどうか判断することができます。
proc hoge[T,U]() = when T == U: assert( false ) echo "なにかの処理" hoge[int,string]() # 出力:「なにかの処理」 hoge[int,int]() # assertによるエラー
できました。hoge[int,string]()
では型が違うのでecho "なにかの処理"
が実行され、hoge[int,int]()
ではassertによるコンパイル時のエラーが発生しました。
しかし、次の場合はどうでしょうか。
type Base = ref object of RootObj Derived = ref object of Base hoge[Derived,Base]() # 型が違うのにエラー
Derived
とBase
は別の型ですが、is演算子でDerived is Base
とするとtrueになります。これはis演算子が型が同一もしくは基底クラスツリーに存在するかどうかで判定を行っているからです。
意味論的に確かにDerived
はBase
でもあるので正しいのですが、今必要なのはDerived
とBase
を別の型として判定する方法です。
正解
では、答えです。
proc hoge[T,U]() = when T is U and U is T: assert( false ) echo "なにかの処理" type Base = ref object of RootObj Derived = ref object of Base hoge[Derived,Base]() # エラーにならない
is演算子の左右の項を入れ替えて2回判定し、2回ともtrueだった場合は完全に同じ型と判断できます。
is演算子は左辺値と右辺値が同じ
もしくは、左辺値の基底クラスツリーに右辺値が含まれる
かどうかで判定しているので、
左辺値と右辺値を入れ替えてもう一度判定することで、それぞれが継承関係にあったとしても必ずどちらかの判定でfalseになり、別の型として判定できるわけです。