Nimのややマイナーな言語仕様(funcとnot nil）

## はじめに
自分なりに検証したメモに近いので、あんまり役に立たない記事です。

## まとめ
- `func`
    - 副作用ありの場合にコンパイルエラーになってくれる。
    - ただし、仮引数がポインタ(`var`や`ptr`)や、参照型(`ref`)の場合は、この仮引数を変更しても検知はしてくれない。
- `not nil`
    - 指定した型に`nil`を代入される可能性がある場合にエラーになる。

## func 及び noSideEffectプラグマ
公式マニュアルは以下のとおり。（Google翻訳さん）
要するに**副作用がないと定義しているのに、副作用があった場合はコンパイルエラー**を出してくれる機能の模様。


>noSideEffectプラグマは、副作用を持たないようにproc / iteratorをマークするために使用されます。つまり、proc / iteratorはそのパラメータから到達可能な場所のみを変更し、戻り値は引数にのみ依存します。そのパラメータが型var T、ref T、またはptr Tをいずれも持たない場合、これはロケーションが変更されないことを意味します。コンパイラがこれを検証できない場合にproc / iteratorに副作用がないとマークするのは静的エラーです。

>特別な意味規則として、組み込みのdebugEchoは副作用がないように見せかけるので、noSideEffectとしてマークされたルーチンのデバッグに使用できます。

>funcは副作用のないprocの糖衣構文です。

`func`は`{.noSideEffect.}`のシンタックスシュガーなので、基本的には`func`を用いればよいと思われる。

では、この機能「ほんまか？」を検証する。

### echoやファイルI/Oするケース
```nim
func fileIOFunc(filename:string) = filename.fileWrite("Side Effect")
# Error: 'fileIOFunc' can have side effect
```
うむ、ちゃんとエラーになる。

### 関数外の変数の参照するケース
```nim
let i = 0 #iへは再代入はできないが、エラーになる。
func varFunc(arg:int):int = i + arg
# Error 'varFunc' can have side effect
```
うむ、ちゃんとエラーになる。
ちなみに上記の`let i = 0` を`const i = 0`に変更すると、コンパイルエラーにはならない。

```nim
const i = 0
func varFunc(arg:int):int = i + arg # Ok
```
### 呼出すプロシージャが副作用をもっているケース
```nim
let i = 0
proc subProc(arg:int):int = i + arg
func callProcFunc(arg:int):int = subProc(arg)
# Error: 'callProcFunc' can have side effect
```
ふむ、ちゃんとエラーになる。

### 引数を変更しちゃうケース
```nim
func varArgFunc(arg:var int):int = 
  arg = arg + 1
  return arg
var i = 1
echo varArgFunc(i) # 2
```
コンパイルエラーにはならず。これは前述の
>そのパラメータが型var T、ref T、またはptr Tをいずれも持たない場合

に該当しないので、副作用ありと判断してくれない模様。ここまで検知はしてくれないか・・・。ちょっと残念？。`ref T`型を引数に渡すことは普通にありえるがこんなもんなんだろうか？まぁ、わざわざ`var`をつけるんだから、そりゃそうか･･･という気もしますが。

## not nil制約

Kotlinのスマートキャスト機能はスバラシイ。最近のプログラミング言語はNull安全がモダンらしい。
Nimにも`not nil`というキーワードを型に指定することができ、nilを許容しない型を定義できる。

### not nil利用例
```nim
import strformat
# ドキュメントに記載ないが、実験的機能のためプラグマが必要
{.experimental. "notnil".} 

type
  # Nil許可型
  NilablePoint = ref object
    x,y:int
  # Nil不許可型
  Point = NilablePoint not nil

  # 以下の様に継承関係持たせたかったが、うまくいかなかった･･･
  #Point = ref object of NilablePoint not nil

proc `$`(self:NilablePoint):string = fmt"x:{self.x},y:{self.y}"

let p1:Point = nil # nil不許可なのでコンパイルエラー
let p2:NilablePoint = Point(x:1,y:1) #これはコンパイル通る。
let p3:Point = NilablePoint(x:1,y:1) #これもコンパイル通る。
echo p2
echo p3 # $(toString)はNil許可型にバインドされているが、Nil不許可型でもうごく。
```

まず、Kotlinのように`Nil不許可型　-> Nil許可型` の継承関係を作ろうとしたが、それをするとなぜかNil不許可型にnilを代入してもコンパイルエラーにならなかった。
おそらく、親の型の特性を引き継ぐ方が優先されている？（ちょっと、良くわからない挙動。。。）

なので仕方なく、Nil許可型のエイリアスとしてNil不許可型を定義して、`not nil`制約をつけてみると、nilを代入すると正しくコンパイルエラーになってくれた。

### nilを返し得るプロシージャとの関係
次にnilを返し得るプロシージャの戻り値を、Nil不許可型で受けたとき

```nim
# nilを返しうるプロシージャ
proc nilableProc(n:int):NilablePoint=
  if n > 0 :
    NilablePoint(x:n,y:0)
  else:
    nil

# Nil不許可型にバインドされるプロシージャ
proc showX(self:Point) = echo(self.x)

let p1:Point = nilableProc(1) # Error: cannot prove 'nilableProc(1)' is not nil
let p2:NilablePoint = nilableProc(1) # これは通る
p2.showX # これは通らない #Error: cannot prove 'p2' is not nil

```
エラーが出てほしいところでは、期待通り以下のようなコンパイルーエラーが発生してくれるので、Nil安全な模様。
>Error: cannot prove 'nilableProc(1)' is not nil
>(Nilにならないと証明できない）


### で、使いやすいのか？

Kotlinと比べると煩雑＆中途半端な気がします。実装の仕方によってヌルポを軽減はできるけれど、完全抑止は難しそう。
基本的にはNil許可（あえて`not nil`と付けないといけない）なんで、今の所オマケっぽい機能に感じました。

まだ「実験的」機能のため、今後に期待。