Flat Leon Works

アプリやゲームを作ってます。

【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]() # 型が違うのにエラー

DerivedBaseは別の型ですが、is演算子Derived is Baseとするとtrueになります。これはis演算子が型が同一もしくは基底クラスツリーに存在するかどうかで判定を行っているからです。 意味論的に確かにDerivedBaseでもあるので正しいのですが、今必要なのはDerivedBaseを別の型として判定する方法です。

正解

では、答えです。

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になり、別の型として判定できるわけです。