関数型に想いを乗せて
今日はちょっと興味深いプログラムの話をしよう。関数型プログラミングについてだ。一般的には、C言語の手続き型だとか、Javaのオブジェクト指向型だとか、いうプログラミング言語が有名なところだけど、それとは違った F# という関数型プログラミング言語がある。
プログラムといっても、普通の言葉、こんな風に語っている自然言語とあまり変わらない。いや、変わらないようにプログラミング言語が作られているってのが正しいかもしれない。最初は英語圏の人達が英語で語れるようにプログラミング言語を作ったところが始まりなので、if とか for とかいう予約語が英語なのはそのためだ。フランス語とかドイツ語が母国語の人達はどうしているのだろうと思うだろうけど、彼らも英語風に作られたプログラミング言語を使っている。もちろん、いま喋っている私は日本語を母国語にしているけども、英語風のプログラミング言語を使っている。
そんな訳で、いつも自分が喋っている言葉とは違う単語や文法を使いながらコンピュータと話すわけなんだけど、同じようにプログラミング言語を使うフランスやドイツの人達とも話すことができるのがコンピュータの面白いところだ。コードで語る、っていう手合いと同じだろう。ほら、音楽で語ったり、絵画で語ったりするのと同じだ。プログラムは芸術ではないけれど、ときには芸術のようにそれぞれの母国語を使わなくても相手に通じることがある。
そうだな。うまい例を思いつかないけど、ひとつの手続き言語から始めてみようか。先に話したけど C 言語の場合は、関数をこんな風に書くんだ。
```C
int add(int x, int y) {
return x + y;
}
```
とっても簡単で、x と y という変数を足し算する関数なんだけど、いつも x + y という答えが出てくる。当たり前のようにみえる。例えば 1 + 1 は 2 のように、2 + 2 は 4 になる。add( 1, 1 ) は 2 になるし、add( 2, 2 ) は 4 になる。とっても簡単な足し算だ。
関数名、そう、add ってのが関数名というのだけど、関数の名前も add という英語で「足す」という意味が付けられているので、非常に覚えやすい。うん、あたり前すぎて、つまらないように見えるけど、ちょっと次のコードを見てよ。
```C
int add(int x, int y) {
return x * y;
}
```
ちょっとだけコードが変わっている。そう、x + y が x * y に変わっている。つまり、足し算が掛け算に変わっている。けれども、add という関数名は同じままだ。add という名前のままでは変だよね、というのはちょっと脇において、この add 関数を使う側を考えてみよう。さっきの add( 1, 1 ) と add( 2, 2 ) を使ってみると、どうなるかってことだ。
```C
int a = add( 1, 1 ); // a は 1 になる
int b = add( 2, 2 ); // b は 4 になる
```
このコードでは既に x * y に変えてしまったときのコードになるけど、実際はどっちの add なのかわからないのだ。もともとの x + y のほうかもしれないし、x * y のほうかもしれない。頼りになるのは add という関数の名前だけど、同じだったら、それは変更する前の x + y のままでいて欲しいよね。僕もそう思う。だから、関数ってのは、いつも同じ答えを返してほしいものなんだ。これは C 言語での関数の扱いだけど、これが F# ってのを使うと、こんな感じになる。
```F#
let add x y = x + y
let a = add 1 1 // a は 2 になる
let b = add 2 2 // b は 4 になる
```
add 関数の呼び方が C 言語とはちょっと違っているけど、まあ、似たようなものさ。C言語からしたら、いきなり中国語を喋っているような気分になるけど、これも add という英単語を使えるので、フランス人でもドイツ人でも読める。もちろん、中国人や日本人でも読める。
add 関数を呼び出したときに、add から想像できる答えが返ってくる。
ちょっと気分を変えて、Java でも書いてみよう。
```Java
class Calculator {
int add(int x, int y) {
return x + y;
}
}
```
Java の場合は C 言語とは違って、class ってんでくるまないといけない。クラスってのは、学校のクラスと同じでひとまとまりのグループのことだね。Calculator っていうクラスを作って、その中に add という関数が入っている。add を呼び出せば x + y の結果が返ってくるのは C 言語と同じだ。このクラスを使う時はオブジェクトってのを使う。そう、聞いたことがあるかい? オブジェクト指向ってやつだよ。
```Java
Calculator calc = new Calculator();
int a = calc.add( 1, 1 ); // a は 2 になる
int b = calc.add( 2, 2 ); // b は 4 になる
```
クラスがあるのに、何故 new ってのを使って新しいオブジェクトをつくらないといけないのか。ちょっと不思議だよね。学校のクラスがあるのに、もうひとつ新しいクラスを作らないといけないみたいでちょっと変だ。そうだな、想像してみて欲しい。クラスの中に裏掲示板があって、クラスの仲間が数名集まっている状態みたいなものさ。
クラスメイトにヒエラルキーがあったりして、大変だよね。いや、それはアニメだけの話かな。けれども安心して欲しい。プログラミング言語の中では、とくにヒエラルキーってものがあるわけじゃない。単に「神」があるだけさ。Java にも神がいてね。神オブジェクトって形で君臨してたりするんだけど、これはまたの機会にしておこう。ややこしい宗教の話になってしまうからね。
話を元に戻すと、Java でも add という関数...メソッドという呼び方をされることが多いのだけど、この関数を呼ぶと、C 言語と同じように 1 + 1 や 2 + 2 の答えが返ってくる。
これは C 言語と同じなんだけど、Java の場合はもうちょっと不思議なことができるんだ。
```Java
class Calculator {
int counter = 0;
int add(int x, int y) {
counter++;
return x + y + counter;
}
}
```
こんな感じで、add 関数を呼び出した数を数えるための counter を付けておくんだ。そうすると、不思議な事に同じように add( 1, 1 ) を呼び出しても、返ってくる答えが違うんだ。
```Java
Calculator calc = new Calculator();
int a = calc.add( 1, 1 ); // a は 3 になる
int b = calc.add( 1, 1 ); // b は 4 になる
int c = calc.add( 1, 1 ); // c は 5 になる
```
同じ add( 1, 1 ) を呼び出しても、返ってくる答えが違う。3, 4, 5 と変わっていくんだ。これは counter という値が、Calculator クラスの中にあるからなんだ。
副作用といったりするのだけど、つまりは心変わりだ。同じように add 関数を呼び出していても、Calculator クラスにある counter という値が変わっていく。いつもと同じ言葉を語り掛けているのに、だんだんと違った形になってしまうの Calculator クラス。ひょっとすると、無限の回数 add 関数を呼び続けてしまうと、Calculator クラスは破綻してしまうかもしれない。なんて、恐ろしい事だろう。永遠の愛なんて存在しない。
そんな感じがするだろう?
ところがだよ、F# の場合は、そんなことはできないんだよ。関数型言語の場合は、add 関数を呼び出したら、必ず 1 + 1 の答えを返してくれる。いつでもだ。Java のように無限に呼び出しちゃだめってわけにはならない、関数型プログラミング言語ならば無限に呼び出したっていい。
```F#
let add x y = x + y
```
もっと、素晴らしいことを教えよう。
```F#
let add x y = x + y
let a = add "i love " "you" // a は "i love you" になる
let b = add "i need " "you" // b は "i need you" になる
```
add という関数をそのままにして、そのまま文字列を足し算することもできるんだ。数値の場合は 1 + 1 は 2 のようになるけど、文字列の場合は "i love " + "you" は "i love you" になる。add 関数の定義...そう中身は変わらないのに、いつでも僕の想いが伝わってくるような感じじゃないか。そう、関数型プログラミング言語は、いつでも同じ答えを返してくれる。
でもね、この数字を返す add 関数と、文字列を返す add 関数は実は別物なんだ。見た目は同じ add にみえるけど、実は別人だ。ちょうど、苗字が同じだけど、違う人物だというのと同じだ。だから、両方をいっぺんに愛することはできない。
```F#
let add x y = x + y
let a = add 1 1 // a は 2 になる
let b = add "i love " "you" // ここはエラーになる
```
そう、僕の気持ちを 1 + 1 で伝えたあとには、文字列を使って "i need you" と伝えることはできない。同じような外見をしているのに、同じ苗字なのに、違う人物なんだ。そりゃ、実はそうなのだけど、現実とはそういうものだけど、でもひょっとしたら仮想空間ならば、と思わないか、いや、それは失言だ。ごめん。何よりも、唯一の君を愛してないといけない。だから、僕は 1 + 1 の答えか、 "i love you" のどちらかしか選べない。
そう、夏目漱石が言った、「月が綺麗ですね」という言葉から得られるのは、君を愛しているという答えと、
「満月の照度は約0.2〜0.3ルクスです。
これは雲の有無や観測条件によって若干変動しますが、一般的にはこの範囲で認識されています。
- 照度とは:光源が照らす面の明るさを示す単位で、1ルクスは1平方メートルあたりに1ルーメンの光が届く状態です。
- 満月の照度:
雲ひとつない満月:約0.3ルクス
少し雲がかかった満月:約0.2ルクス
薄雲のある満月:約0.1ルクス
- 比較対象:
太陽光(晴天時):約100,000ルクス
読書に必要な照度:300〜500ルクス
街灯の下:約10〜20ルクス
つまり、満月の光は太陽光の約50万分の1の明るさであり、読書には不十分ですが、暗い山道を提灯なしで歩ける程度の明るさです。
二つの答えは同時に得られない。
ああ、そう、私が愛するのは君なのに。
【完】




