この記事はnim 1.6.14で動作を確認しています。
これは何?
NimをPythonと同じ感覚で使っていると、int * float
のような計算の時にint
が自動的にfloat
にならなくて、毎回float(int)
と書くのが少しめんどくさいって思うことがある。ここら辺を暗黙的にやってくれるようにしたい。
結論
converter
を定義してあげることで、暗黙の型変換が行われるようになる。
converter toFloat(x: int):float = float(x)
# 基本
var a = 30
var b = 2.5
echo a * b #75.0
#関数の呼び出し
proc test1(x:float)=
echo x
test1(a) #30.0
#同名関数がある場合の優先度
proc test2(x:int)=
echo "int"
proc test2(x:float)=
echo "float"
test2(a) #int
#autoで引数が定義されている関数への優先度
proc test3(x:auto)=
echo typeof(x)
test3(a) #int
proc test4(x:float)=
echo "float!"
proc test4(x:auto)=
echo typeof(x)
test4(a) #int
また、以下はコンパイルエラーとなる。
暗黙的に変換して実行できる同名関数が複数ある場合
converter toString(x: int):string = $x
proc test5(x:float)=
echo "float"
proc test5(x:string)=
echo "string"
var a = 30
test3(a) #コンパイルエラー。
Error: ambiguous call; both Main.test3(x: float) [proc declared in Main.nim(25, 6)] and Main.test3(x: string) [proc declared in Main.nim(27, 6)] match for: (int)
複数のコンバータを経由すれば型変換できるもの
var a = 30
converter toFloat(x: int):float = float(x)
converter toString(x: float):string = $x
proc test6(x:string)=
echo "hello"
test4(a) #コンパイルエラー
Error: type mismatch: got <int>
もとから暗黙に変換されるもの
公式ドキュメントによると、以下の関数がtrue
となるような型a
と型b
は、型a
から型b
に暗黙的に変換される。
proc isImplicitlyConvertible(a, b: PType): bool =
if isSubtype(a, b):
return true
if isIntLiteral(a):
return b in {int8, int16, int32, int64, int, uint, uint8, uint16,
uint32, uint64, float32, float64}
case a.kind
of int: result = b in {int32, int64}
of int8: result = b in {int16, int32, int64, int}
of int16: result = b in {int32, int64, int}
of int32: result = b in {int64, int}
of uint: result = b in {uint32, uint64}
of uint8: result = b in {uint16, uint32, uint64}
of uint16: result = b in {uint32, uint64}
of uint32: result = b in {uint64}
of float32: result = b in {float64}
of float64: result = b in {float32}
of seq:
result = b == openArray and typeEquals(a.baseType, b.baseType)
of array:
result = b == openArray and typeEquals(a.baseType, b.baseType)
if a.baseType == char and a.indexType.rangeA == 0:
result = b == cstring
of cstring, ptr:
result = b == pointer
of string:
result = b == cstring
of proc:
result = typeEquals(a, b) or compatibleParametersAndEffects(a, b)
まとめると、
- 各種型は親タイプに変換できる
- 整数リテラルは
int
,uint
,float
各種に変換できる - 各種
int
,uint
はbit数が自分よりも大きい型に暗黙に変換できる -
float32
とfloat64
はそれぞれ暗黙に変換できる -
seq
とarray
はその中身の型が一致しているopenArray
に変換できる -
char
のarrayはindexが0始まりのときcstring
に変換できる -
cstring
とptr
はpointer
に変換できる -
string
はcstring
に変換できる -
proc
は引数の型が一致している場合は変換できる。compatibleParametersAndEffects(a, b)
は現在使われていないようだ。
ところで、上のコードだとaがarray[char]
でbがopenArray[char]
のとき変換できなそうなのだが、普通にできるようだ。
参考・引用文献
Nim Manual Convertible relation
追記
Nimのstd/lenientopsを使えば、floatとintの演算は大体定義されていそうなので一番最初に上げた目的だけなら、そちらをimportする方が楽。
std/lenientops