目次

プロシージャ

プロシージャは他の多くの言語で言うところの関数です。プロシージャはprocを使って新しく定義できます。

proc add(a, b: int): int =
  a + b

echo add(1, 2) #=> 3

procは値を返すことが出来るので、まるで関数のようです。Nimにはfuncというキーワードもあります。これは関数型プログラミングの純粋関数により近いものです。また、methodというキーワードもあります。現段階ではfuncmethodは忘れていおいてprocに注力します

戻り値

先のaddの例では、最後の式であるa + bが戻り値になりました。プロシージャの戻り値がどのように決定されるかは、次のルールに従います。

先のaddの例では、最後のルールが適用されました。他のルールが適用されるように書き直すことが出来ます。

resultという名前の変数が使用されていた場合、その値が戻り値となる:

proc add(a, b: int): int =
  result = a + b

return文がある場合、それに続く式の値が戻り値になる:

proc add(a, b: int): int =
  return a + b

式を伴わないreturn文がある場合、return resultと同じ意味になる:

proc add(a, b: int): int =
  result = a + b
  return

このプロシージャをadd(1, 2)と呼び出した場合、いずれも最初の例と同じである3が戻り値になります。

パラメーター

パラメーター(parameters)とはプロシージャの引数を指します。プロシージャの呼び出し時に渡される値はアーギュメント(arguments)と読んで区別することが出来ます。Nimに限ったことではなくプログラミング一般において、必要なときにのみparametersとargumentsを使い分けることが多いです1)。どちらを指しているかは文脈から明らかなことが多いので、最近は単に引数とだけ呼ばれるのが普通です。

先のaddを例に取ると、aとbがパラメーターで、その型はintです。パラメーターはイミュータブルです。つまり、プロシージャ内で変更することは出来ません。

次のコードはコンパイルに失敗します。

proc setToOne(x: int) =
  x = 1    # コンパイルエラー

エラーメッセージは次のとおりです。

Error: 'x' cannot be assigned to

varキーワードを付けることで、プロシージャ内で値を変更できます。

proc setToOne(x: var int) =
  x = 1
  
var x = 100
setToOne(x)
echo x   #=> 1

プロシージャ内での変更が呼び出し元にまで影響を与えている、すなわち、参照渡しとなっていることに注意してください。

戻り値を無視する

Nimでは、値を返すプロシージャの呼び出しで、その戻り値を無視することは出来ません。

proc add(a, b: int): int =
  a + b

add(1, 2)   # コンパイルエラー

このプログラムはコンパイルできません。エラーメッセージは次のようになります。

Error: expression 'add(1, 2)' is of type 'int' and has to be used (or discarded)

エラーメッセージで指摘されている通り、戻り値を使用するか、破棄される(discarded)必要があります。破棄するにはdiscard文を使います。

discard add(1, 2)

これはコンパイルエラーになりません。

その他のプロシージャの特徴

プロシージャには便利に使えるようにするために、いくつか便利で重要な特徴があります。

名前付き引数

通常のプロシージャ呼び出しは、引数は左から順番に渡されます。名前付き引数を使うことで、プロシージャ定義の引数の順序に依存せず引数を渡すことが出来ます。

デフォルト値

プロシージャの定義で、パラメーターにデフォルト値を設定することが出来ます。

オーバーロード

パラメーターが異なる、同じ名前のプロシージャを複数定義することが出来ます。

演算子のオーバーロード

+==となどの演算子にプロシージャを定義することで、その演算子を使った式でプロシージャが呼び出されるように出来ます。

前方宣言

プロシージャは、使用されるところでそのプロシージャが見えていなければなりません。プロシージャの本体が見えている必要はなく、そのシグネチャさえ見えていれば良いです。プロシージャを定義せずに宣言だけ行う方法があります。

proc add(a, b: int): int   # 前方宣言

proc addOne(a: int): int =
  add(a, 1)

1)
日本語の古い資料だと仮引数と実引数に分けているものもあります。個人的な意見を言うと、どっちが「仮」でどっちか「実」か覚えておかなければならず、脳みそに無駄な負担かけるので、この名称は好ましくないと思っています。