Edited at

オブジェクト指向から見た、関数型プログラミングについて

前回はこちら…


抽象化について

今回は、抽象化についてを視野にお話を進めたいと思います。


練習

抽象化を実際に練習していきましょう。

私の練習方法ではありますが、次の事を意識して文脈が成り立つかどうかを考えます。

主語を削除しても、その文脈は通じるか?

理由についてはそれほど難しくありませんが、ラムダ抽象を例に根拠を幾つか上げることは出来ると考えます。


最大の抽象化とは?

あらゆる主語がとり得る継承でありながら、最大のポリモーフィズムを生み出す最初にして最大の抽象クラス(interface)宣言は何でしょうか?

頭の中で想像してみて頂くと理解できるかと思いますが、

答えは宇宙(現実)です

interface Universe {}


宇宙の真理

ここまで言うと宗教めいても来ますが、より物理学的で数学的なアプローチで説明することが出来ます。

トーラス構造といいます。

このトーラス構造は、チューリング完全上の計算機構が多重に入れ子になっているという説明もできます。

りんごを例に取ると、りんごの花が咲いて実がなる動きの過程で中に芯が形成され、その芯の中に種が形成されていきます。

出入り口が上下にある渦の中央に別の空間があってその中心の渦の…と続きます。


計算機構について

トーラス構造は、空間に量子という状態を保持できるオブジェクトとして説明が可能です。では空間に一つの量子だけをとり得ることも出来るのではないでしょうか?

目には見えないほど小さい量子の一つだけです。


この考え方を機械上で考えてみる。

コンピュータ上で、1ビットの概念を破壊することは容易ではありません。

そのままでは小さすぎて有用性が見えないため、現実世界で破壊し辛い概念を用います。

例えば、数です。コンピュータ上では、プリミティブな値として扱われるものですが、具体的な値はと言われる通りで扱いづらいものなのです。


値を一つだけ取りうる計算機構

例えば、値一つを取る仮想機械(class)を想像してください。

それに対して、どんなメソッドを追加していけばより便利になるでしょうか?

そういう考えに基づいて作られたclassには、大きな処理を一つのメソッドに実装する必要はなくなります。さらに細分化した時、それに名前を与える価値は何でしょうか?

我々は、自明的な数学の記号に、わざわざ命名を与えたメソッドにするようなことをしないことが多いはずです。以下は、javascriptでの例です。

// わざわざ命名する

function add (value1, value2) {
return value + value2;
}

add(1, 2) /// 3

// 命名しないまま、関数の実行を考える
(function (value1, value2) {
return value1 + value2;
})(1, 2)

// 上記の関数を、引数の一つをオブジェクトとした
// メソッドとして実行できないか考える
[1].map(function (value1, value2) {
return value1 + value2;
}.bind(null, 2))[0];

// 上記をもっと数学的な射を表す記述に変えてみる
/*
[1].map((x, y = 2) => x + y)[0]
*/

[1].map(x => y => x + y).map(y => y(2));

つまり、ある関数を適用するメソッド、mapを使うことで、無名のメソッドが実装できている訳です。

あえて引数2つを取ったうちの一つに値を設定しているのは、記述による表記揺れを防止するためです。

この例は、分かりやすく極端にしているため、実際にはもっと短縮した表現を使います。


計算機構の無名関数は、どうやって実行されているのか?

論より証拠ですが、こんなものを書くと分かりやすいかも知れません

class List extends Array {

constructor (...a) {
Object.assign(this, a);
}
map (func) {
let result = [];
for (let i = 0; i < this.length; i++) {
result[i] = func(this[i]);
}
return new this.constructor(...result);
}
}

map するためのメソッドを、自力で実装すると、引数にとった関数の第1引数に自身の持っている値を入れて計算しているというのが、明白だと思います。


一番大きいものを引数に取る

よくありますが、こんなコードを思い浮かべてみてください。

(function (state) {

return state;
})(universe);

(state => state)(universe);

宇宙そのものを関数に入れて実行するという事は、出来るということになります。

そうするとこれは、最初の方でインターフェースで扱うしか無かったオブジェクト指向とは違い、宇宙そのものを扱う抽象関数が記述可能になるということを言っていることと同じになります。


メリット・デメリット

オブジェクト指向は、フォールダウン型の大規模開発に向いていますが、その実細かいメソッドの実装の連続が変更に耐えうるために必要な書き方ということです。

しかし、メソッドに一々命名が伴います。

分かりやすいのですが書く方にとってはあまり優しくないというのも事実です。

関数型は、小規模から開発して規模に合わせたスケーリングを可能にします。

しかし、あまりにも小さいサイズの抽象を扱いすぎることで、静的型付け言語では、少々の苦しい言い訳を迫られることもあります。

それが、なれない人にとっては読みづらさにもなります。

扱う抽象が増えれば、計算機構を増やさなくてはなりません。


宇宙を引数に取る関数は最初と最後だけ

関数型プログラミングでは、副作用(値の変更)は最初と最後だけに発生し、関数の実行結果を別の関数の引数に代入していくという手法を取ります。

無名であれば、メモリ上に残る懸念もありません。

生じて消える生き方に近い潔さが、関数型プログラミングの魅力かもしれません。


最後に

Haskellの進展には目を見張る物がありますが、そろそろRustやGoを勉強しようと悩む私です。

もう少ししたらC2出ますが、Rustを使ったOSのためのフレームワークとか作るのも面白そうだと思い始めました。

そう言いながらDAWの自作が先になる気も? しています。

ここまで読んでいただき、ありがとうございました。

宣伝にはなりますが、値を一つだけ取る計算機構を扱いやすくしてみたライブラリが以下です。

よろしければ、使用感や要望などもお願いいたします。

m(_ _)m

losand

dsand