例: 高階関数パズル

はじめに次のような型定義を導入しましょう:

typedef I (a:t@ype) = a -<cloref1> a

型 T が与えられた時 I(T) は、与えられた入力である型 T の値を、出力としての型 T の値へ写像するクロージャ関数です。 型 I(T) の関数 f が与えられた時、 単に与えられた引数に f を2度適用するような別の関数を作ることができます。 次の関数テンプレート twice は左記に説明した関数を正確に構成しています:

fn{a:t0p} twice (f: I(a)):<cloref> I(a) = lam (x) => f (f (x))

twice を使った興味深いコードを見てみましょう。 それはパズルのようにも見えます:

// typedef I0 = int typedef I1 = I(I0) typedef I2 = I(I1) typedef I3 = I(I2) // val Z = 0 val S = lam (x: int): int =<cloref> x + 1 val ans0 = twice<I0>(S)(Z) val ans1 = twice<I1>(twice<I0>)(S)(Z) val ans2 = twice<I2>(twice<I1>)(twice<I0>)(S)(Z) val ans3 = twice<I3>(twice<I2>)(twice<I1>)(twice<I0>)(S)(Z) //

上記のコードを読みやすくするために、型宣言 I0, I1, I2, I3 が導入されていることに注意してください。

明らかに Z は整数 0 を表わし、S は整数の後者関数 (Successor Function) を表わしています。 また ans0SZ に2度適用した結果なので、2 に等しくなります。 S2S を2度適用する関数とします。 ans1S2Z に2度適用した結果となり、すなわち 4 に等しくなることは明確です。 もう少し考えると、 ans2 の値が 16 になることが分かるはずです。 ans3 の値はいくつでしょうか? 一般に、ans0, ans1, ans2, などの数列の n 番目の値はいくつになるのでしょうか? これらの疑問点は興味を持った読者への練習問題とします。

この章のコード全体とテストのための追加コードは オンライン から入手できます。 (訳注: このコードを atscc でコンパイルする際には -DATS_MEMALLOC_LIBC オプションが必要です。)