オブジェクト指向でモナドするならメソッドチェーンで書くと自然になるかも
オブジェクト指向の言語でモナドっぽいことをするなら、メソッドチェーンできるように実装すると自然になるかなと思った。
JavaScript では Promise
がモナドなのだけど、 bind
的なことをしているのは then
なわけです。
// doSomethingAsync の型が number -> Promise<number> だとして
Promise.resolve(1)
.then(doSomethingAsync)
で、 Promise
といえば then
のメソッドチェーンで(めんどくさいけど)良い感じに書ける。
そこで、Promise
と同じ気持ちで副作用を扱うモナドが作れる side-eff というライブラリを作ってみた。
例をつけて説明します。
まず、 SideEff
を継承したクラスの中で affect
メソッドに副作用のある関数を定義します。ここでは Message
クラスがそれ。 affect
メソッドの中でグローバル変数にアクセスして値を書き換えます。
Message
クラスのコンストラクタは 2 つの引数を受け取ります。第一引数が外に取り出せる値で、第二引数が affect
メソッドに渡す値です。
次に、純粋な関数をいくつか定義します。例では、 add5
, multple2
, square
です。それぞれの関数は Message
のインスタンスを返すようにします。
それから、メソッドチェーンを作ります。 SideEff
の affectThen
は副作用のある affect
をしれっと実行してから、値を"むいて"次の関数に渡します。たとえば.affectThen(add5)
の挙動は、 affect('Start')
を実行してから、 add5 に 1 を渡し、 new Message(6, 'Add 5')
を返します。
メソッドチェーンの最後 .unwrap()
は affect
を実行してから値を取り出すことをします。 変数 result
には 144 (= ((1 + 5) * 2) ^ 2) が入ります。
メソッドチェーンの背後で affect
が呼ばれているので、グローバル変数 message
が変更され、その値は最終的に、
Start Add 5 Multiple 2 Square
となっています。
説明するとなんか厄介ですが、 add5
, multple2
, square
達は純粋関数でありつつ、副作用の扱いを Message
が一挙に引き受けて汚れ役になってくれているのがおわかりいただけますでしょうか。
このライブラリを実務で使うかというと別に使わないのですが、一つのデザインパターンとしてはありかなと思いました。