ツクールMVで関数型プログラミングに入門する その2

はいどーも関数型ニートです。
本日も続けて関数型プログラミングに入門していきますよ。
例によって勉強中のメモみたいなところが多分にあるので、内容の正確性は保証しません。

緩くつなげるパイプライン

前回の記事でメソッドチェーンについて書きました。
これはある特定の型でラップし、関数の実行結果もその特定の型を返し続けることで実現しています。
言い換えれば、その特定の型以外ではチェーンを形成できないことになります。
それでは不便なときもあるだろうということで考えられたのが「緩くつなげるパイプライン」です。
ある関数aの戻り値が、関数bの引数として受け取れるならば、関数aと関数bはパイプラインとして使えます。

例えば前回の記事で作った

こいつは文字列を返します。
パイプラインとして使うなら、文字列を受け取る必要があります。
例えばデコレートする関数ならこんな感じ。

この2つの関数を用いる例としては以下のようなものが考えられます。

カリー化してみる

上記のコードを見ても僕は正直「ナンノコッチャ」という感じでした。
一体全体なんの役に立つのかわからなかったのですね。
では次にカリー化を見ていきます。
Haskellではカリー化は勝手に有効になりますですが、JavaScriptではそうではありません(引数が足りない場合はundefinedが渡される)。
というわけで、カリー化を簡単におこなえるようにライブラリを導入します。
その名もRamdaです(前回の記事で使ったLodashとは違いますので、その点ご注意ください)。
例えばこんな感じでカリー化します。

decorateStringをカリー化し、actorNameDecorate関数を生成しています。
この関数に文字列の引数を渡して呼ぶと、その文字列でデコレートされた1番目のアクターの名前が返されます。

何の役に立つのか

で、やっぱりこれが何の役に立つのか僕はよくわかりませんでしたw
紹介されていた例では「ファクトリメソッドのように使える」とありました。
確かにその例を見た感じファクトリメソッドのように使えるのは確かだろうと思ったのですが、「それならファクトリメソッドでいいじゃん」と思ってしまいました。
あるいは、引数の数がめちゃくちゃ多くて使いにくい関数があったとして、それをカリー化して引数の数を減らした場合も役に立つのかなとは思いました(今回の例はこれでしょう。ただ関数を関数でラップするだけで事足りる気もしました。が、それは手続き型のパラダイムなんでしょうかね)。

関数合成を使ってみる

さてお次は関数合成です。
例えばこんな感じ。

各関数に参照透過性があるとき、各関数が要求するインターフェイスを満たすなら、それらの関数を合成することができます。
例えば
countArrayLengthは配列を受け取り、数字を返します。
createStarStringは数字を受け取り、文字列を返します。
encodeURIは文字列を受け取り、文字列を返します。
これらの関数は全て参照透過性があるため、中身(実装)は関係ありません。
Aを受け取りBを返すというインターフェイスさえわかっていれば合成可能です。
Haskellのような純粋関数型言語では参照透過性や副作用を持たないことが強制されますが、JavaScriptで関数を作成する場合はプログラマが責任を持って参照透過性や副作用を持たないコードを書く必要があります。

疑問点

R.composeを見ればわかるように、最初に実行させたい関数は最後に登録する必要があります。
これが僕としてはあまり直感的ではなく、逆順に登録したいなーと思いました(pipeという関数があるにはあります)。
ただわざわざこういった順番になっているのにはそれなりの理由があるのだろうなとも思いましたが……。
もう少し勉強が必要ですね。
なおpipeを使うとこんな感じ。

僕的にはこっちの方が直感的です。
Lodashではflowで合成するようですが、flowはpipeと同じ感じで使えるみたいですね。

前回の記事で作ったものを合成する

前回の記事でこんなコードを書きました。

これを合成します。
今回はLodashを使います(特に意味はありません)。

コメントで示したコードをコンソール等で呼ぶと、最初のメンバーの名前が表示されます。
オブジェクト指向のパラダイムならば、Game_PartyのprototypeにfirstMemberNameメソッドを追加するとかでしょうね。

不純なコードと純粋なコード

ここで改めて不純なコードと純粋なコードを考えてみます。
不純なコードとは、以下のようなものです。

・外部に観測可能な副作用を与える(「観測可能」という点に注意してください。観測できない状態の変化は許容されます)
・関数スコープを超えたデータにアクセスする(例えばグローバルオブジェクト。もっと大きなもので言えばDB等)

純粋な関数は使いやすいです。
いつでも何度でも気軽に呼び出すことができるからです。
ですが不純な関数を全くなしにすることは難しいです。
なので、せめて不純な振る舞いと純粋な振る舞いを分離しましょうね、というのが一般的のようです。
また、合成の途中で例えばconsole.logで内容を確認したくなることもあるかと思います。
そんなときは例えばtap関数を用いることで、void関数でも問題なく使用することができます。

nullチェック

ある関数が有効な値とnull値(やそれに相当する値)を返すとき、その関数を利用する関数はnull値に対する処理を書かなければなりません。
例えば以下のようなコードがあったとします。

この関数はundefinedを返す可能性があります。
従って、例えば以下のようなコードは書くべきではありません。

上記のコードは例外を投げる可能性があります。
ではどうすればよいかと言えば、nullチェックをすることです。

これでこの関数は安全に使用することができます。
……が、このnullチェックは非常にめんどくさいです。
例えばC#ではこれと似たようなコードをNull 条件演算子を用いて以下のように書くことができます(擬似的なコードで、C#のコードではありません)。

もしもfetchPartyMember(num)の結果がnullならば、levelUp関数は実行されずに終了します。
ただこのnull条件演算子を用いたとしても、「利用者側がnullを意識しなければならない」ことには変わりがありません。
これはやっぱりバグのもとです。
実際、僕が業務で書いたコードでは「利用者がnullチェックをしなければならないコードは書かないでくれ。安全に使用したい」と言われたこともあります。

nullを意識しないように書いてみる
武器名を取得したいとき、Lodashを使ってnullをなるべく意識せずに書けるようにしてみものがコレです。

武器名の取得に失敗したとき(武器を装備していなかったとき)は「なし」が返ってきます。
でも正直これすごい微妙なコードですよね。
そもそもactor.weapons()がnullだった場合どないすんねんっちゅー話でもあります。

値をラップしてみる

まずは値をラップしてみたいと思います。
で、値をラップできるラッパークラスがほしいのですが……僕がコードを書くよりも既存のコードを流用した方がよさそうな気がしたので、以下の記事のコードを拝借することにします。
https://qiita.com/To_BB/items/aa68027a9f319bce7a33
fanctor.jsの項目のWrapperクラスをそのままコピペしてください(wrapとfmap関数も必須です)。

試しに使うとこんな感じです。

_.identityは引数を返すだけの関数です。
この場合だと「こんにちはツミオです」を_.identity関数に渡したことになります。
で、引数をそのまま帰すので結果として「こんにちはツミオです」が返ってきます。

次はラップされた値100と3を足してみたいと思います。

fmapは新たなラッパーでくるんだ値を返す関数です。
つまりoneHundredの値を直接変えるわけではなく、103をくるんだ新たなラッパーを生成しています。
なおconsole.logがなんか気に入らないなら、こんな感じでしょうか。

あとは_.tap使ってなんやらできそうな気もしました。

もう一つの例はこんな感じ。

ウーンなんか汚いですね。
まあ最初はこんなもんでしょう。そのうちがんばります。

モナドを使ってみる

同じサイトのMaybeモナドを使ってみます。
Maybeモナドを使ってラップされた5と数字の3を足してみます。

ご覧の通り、途中で値がnullになっても安全に関数を実行できます。
これをツクールMVのオブジェクトにも適用してみます。

注*ただツクールMVのオブジェクトに適用するとき、nullのみ安全に扱えるのでは具合が悪いこともあるので、undefinedも安全に扱えるように僕は改造しておきました。

こんな感じのコードをあらかじめ用意しておき

こんな感じでmember2関数をコンソールとかで実行します。
このときパーティが3人以上なら、3人目のメンバーの名前がコンソールに表示されます。
メンバーがいなくてもエラーは吐かれずMaybe.Nothingが表示されます。
これがもしも

とかのコードなら、3人目のメンバーが存在しない場合はエラーが発生します。

終わりに

今回はごく簡単にMaybeモナドを使ってみました(前半は前回の続きみたいなもん)。
ツクールMVで使える配列もモナドに非常に似ているのですが、どうもこれはモナドと呼ばないようです。
まあ具体的なモナドを僕が自分で作ることはまずないと思うので、既存のライブラリか何かでいい感じのものがあればそれ使いたいかなーというくらいのもんです。
もうちょっとJavaScriptにおける関数型について勉強してみたいですね。

ほなそんな感じでまた。
お仕事も募集中です。

フォローする