Edited at

オブジェクト指向から見た、モナドの有用性について


オブジェクト指向から立ち戻ってみる。

今回は、オブジェクト指向から見る関数型プログラミングということで書いていきます。


オブジェクト指向の「オブジェクト」とは

これについては様々な議論がありますが、オブジェクトは「対象」という訳語を当てるのが正確だと感じています。


対象(オブジェクト)とは値である

これはオブジェクト自身も値であると言っているわけです。

下に簡単な例を Java で示します。

public class Foo {

private int value;
Foo (v) {
this.value = v;
}
public add_foo (v) {
this.value += v;
}
}

public class Test_Foo {
public static void main (String[] args) {
Foo foo = new Foo(5);
}
}

Foo foo = new Foo(5);

を少々回りくどく説明すると、

fooという変数の型はFooで、fooにはFooのインスタンスを代入する。

となります。


このインスタンス、つまり値だよね?

正確には、ポインタになっているはずで指している先にインスタンスを格納しています。


値のメソッドの呼び出しを考える

多分Javaでもよくやります。

文字列操作の時などが例になりますが、

関数は直接結果を返してしまいますが、文字列を便利に取り扱うことが出来るのではないでしょうか?


プリミティブな値にメソッドを追加する

主題はこれです。

3 という値にメソッドを追加してみましょう。

どうすれば出来ますか?

Javaでは以下のような記述で書いたクラスを継承してメソッドを追加していき、

その追加したメソッドを利用するという考え方をしますが、

public class IFT {

private int v;
IFT (int v) {
this.v = v;
}
public int value () {
return v;
}
}

javascriptであればこのように書けます。

class MonoInt {

constructor (v) {
this.v = v;
}
static of (v) {
return new MonoInt(v);
}
get join () {
return this.v;
}
map (f) {
return MonoInt.of(f(this.v));
}
bind (f) {
return this.map(f).join;
}
}
MonoInt.of(3).map(v => v + 3).join; // 6
MonoInt.of(5).bind(v => Monoint.of(v * 8)).join; // 40

Java8などからも使えるようになったアロー関数、λ(ラムダ)が出てきました。

この時、MonoIntは一つだけの値を取り、外からは値は一切参照せず加えることをしないということを条件にして、コードを書きます。


オブジェクトは、一つの圏として解釈できる

オブジェクトは、プロパティという を持ち、メソッドという 自然変換 を実装された概念と言えます。

そして、オブジェクト自身も値だとすると、上のコードを以下のように変えれば…


class Foo {
constructor (v) {
this.value = v;
this.key = "foo";
}
get oo () {
return this.v;
}
set oo (v) {
this.v = v;
}
resetKey (v) {
this.key = "foo" + v;
}
}

class Fix {
constructor (v) {
this.v = v;
}
static of (v) {
return new Fix(v);
}
get join () {
return this.v;
}
map (f) {
return Fix.of(f(this.v));
}
bind (f) {
return this.map(f).join;
}
re (f) {
this.map(f);
return this;
}
assign (o) {
return this.re(v => Object.assign(v, o));
}
}

Fix.of(new Foo(5)).map(foo => foo.oo).map(v => v + 3).join
// 8

Fix.of(new Foo(5)).re(foo => foo.oo += 3).assign(["bar", "baz"])).join
/*
Foo {
"0": "bar",
"1": "baz",
value: 8,
key: "foo"
}
*/

オブジェクトのメソッドが何も返さない時でも、そのオブジェクトを一括で取り扱う関数

re を実装することが出来ました。

これにより、Object.assignを代理で行える assign を実装できています。

Fix本体やFixを継承したクラスを使って、reやmap、bindを内部で使用するメソッドを追加してあげれば、まるでそのオブジェクトが本来持っていたのではないかと思うようにメソッドを実行できます。

この時、代入されたオブジェクト本体には、そのメソッドは無いくても良いわけです。


モナドは難しくない

正確には、endo(redo)を行えるモナドは余モナドと言われているようです。

便利なので、どんどん取り入れて行きたいですね。

訂正や提案などありましたら、真摯に対応します。

最後までお付き合いいただき、ありがとうございました。