😀

Nimのfunc(副作用なし関数)が厳格化できるようになる

2020/10/18に公開

概要

  • Nimで一番単純なプロシージャ定義方法はprocを使用
  • procの代わりにfuncを使用して 副作用なし であることを明確化することができる
  • しかし、これまでNimにおける 副作用なし の基準が緩く、直感と合わなかった
  • v1.4.0以降は 副作用なし の基準を厳格化できる

情報元

Version 1.4.0 released
Write Tracking for Nim (Part 2)

これまでの仕様

  • 単純なプロシージャ定義の例
proc add(a,b:int):int =
    return a + b

echo add(1,2) # 3
  • {.noSideEffect.}プラグマを使用して副作用がないことを明確化可能
proc add(a,b:int):int {.noSideEffect.}=
    return a + b

echo add(1,2) # 3
  • proc + {.noSideEffect.}プラグマのシンタックスシュガーとしてfuncを使用可
func add(a,b:int):int =
    return a + b

echo add(1,2) # 3
  • ただしオブジェクトの参照先の値を変えることは 副作用 に含まれていなかった。
# このコードは有効
type
  Node = ref object
    data: string

func setData(n:Node, newData:string) =
  n.data = newData

let n0 = Node(data:"original")

echo n0.data # original
setData(n0,"changed")
echo n0.data # changed

新しい仕様(v1.4.0以降)

次のどちらかにより 副作用なし の仕様を厳格化可能

  • {.experimental: "strictFuncs".}プラグマを使用
  • コンパイル時に--experimental:strictFuncsオプションを使用
test.nim
# このコードはコンパイルできない
{.experimental: "strictFuncs".}
type
  Node = ref object
    data: string

func setData(n:Node, newData:string) =
  n.data = newData

var n0 = Node(data:"original")

echo n0.data 
setData(n0,"changed")
echo n0.data 
  • コンパイラエラーとして以下のメッセージが表示される
  • これにより、 funcの挙動がより直感に合う形になり、関数に副作用がないことを明確化できる
test.nim(7, 6) Error: 'setData' can have side effects
an object reachable from 'n' is potentially mutated
test.nim(8, 4) the mutation is here
  • パラメーターにvarをつけることで、funcの中で意図した副作用を許容することもできる
# このコードは有効
{.experimental: "strictFuncs".}
type
  Node = ref object
    data: string

func setData(n:var Node, newData:string) =
  n.data = newData

var n0 = Node(data:"original")

echo n0.data # original
setData(n0,"changed")
echo n0.data # changed

まとめ

  • v1.4.0以降は 副作用なし の厳格化でfuncをより直感に合う形で使用できるようになった

Discussion