Nimで暗黙的に型変換する

この記事はnim 1.6.14で動作を確認しています。
# これは何？
NimをPythonと同じ感覚で使っていると、`int * float`のような計算の時に`int`が自動的に`float`にならなくて、毎回`float(int)`と書くのが少しめんどくさいって思うことがある。ここら辺を暗黙的にやってくれるようにしたい。
# 結論
`converter`を定義してあげることで、暗黙の型変換が行われるようになる。
```nim
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
```
また、以下はコンパイルエラーとなる。

#### 暗黙的に変換して実行できる同名関数が複数ある場合
```nim
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)`

#### 複数のコンバータを経由すれば型変換できるもの
```nim
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>`

# もとから暗黙に変換されるもの
[公式ドキュメント](https://nim-lang.org/docs/manual.html#type-relations-convertible-relation)によると、以下の関数が`true`となるような型`a`と型`b`は、型`a`から型`b`に暗黙的に変換される。
```nim
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](https://nim-lang.org/docs/manual.html#type-relations-convertible-relation)

# 追記
Nimのstd/lenientopsを使えば、floatとintの演算は大体定義されていそうなので一番最初に上げた目的だけなら、そちらをimportする方が楽。
[std/lenientops](https://nim-lang.org/docs/lenientops.html)