プロシージャ

badge

プロシージャは副作用を許容する関数を意味します。 基本的な定義や利用方法は関数を参照してください。 関数名に対して!をつけることで定義することができます。

proc!(x: Int!, y: Int!) =
    for! 0..x, i =>
        for 0..y, j =>
            print! i, j

プロシージャは可変オブジェクトを取り扱う際に必要となります。 ですが、可変オブジェクトを引数に持つときやプロシージャの定義するだけの場合はプロシージャであるとは限りません。

peek_str s: Str! = log s

make_proc(x!: (Int => Int)): (Int => Int) = y => x! y
p! = make_proc(x => x)
print! p! 1 # 1

またプロシージャと関数はproc :> funcの関係にあります。 そのため、プロシージャ内で関数ブロックを定義することもできます。 しかし、逆はできないので注意をしてください。

proc!(x: Int!) = y -> log x, y # OK
func(x: Int) = y => print! x, y # NG

バインド

プロシージャはスコープ外の可変変数を操作することができます。

x = !0
proc!() =
    x.inc!()

proc!()
assert x == 1

このとき、proc!は以下のような型を持ちます。

proc!: {|x: Int!|}() => ()

{|x: Int!|}の部分はバインド列と呼ばれ、そのプロシージャが操作する変数とその型を表します。 バインド列は自動で導出されるため、明示的に書く必要はありません。

注意として、通常のプロシージャは予め決められた外部変数のみを操作することができます。これはつまり、引数に渡された変数を書き換えることはできないということです。 そのようなことがしたい場合は、プロシージャルメソッドを使う必要があります。プロシージャルメソッドは、selfを書き換えることができます。

C! N = Class {arr = [Int; N]!}
C!.
    new() = Self!(0)::__new__ {arr = ![]}
C!(N).
    # push!: {|self: C!(N) ~> C!(N+1)|}(self: RefMut(C!(N)), x: Int) => NoneType
    push! ref! self, x = self.arr.push!(x)
    # pop!: {|self: C!(N) ~> C!(N-1)|}(self: RefMut(C!(N))) => Int
    pop! ref! self = self.arr.pop!()

c = C!.new()
c.push!(1)
assert c.pop!() == 1