やわらかテック読者になるやわらかテック興味のあること。業務を通して得られた発見。本読んだぞ前回のあらすじ📖www.okb-shelf.work「入門Haskellプログラミング」という書籍を読み続けながら、理解を深めるために自分でFunctorを実装してみました。今回は、Functorに引き続き、Applicativeに挑戦してみます。先に結論ですが、ElixirでApplicativeを作るのは言語の制約上、かなり難しいです。マクロという機能を使えば、似たものには出来ますが、HaskellのApplicativeのパワフルさを完全再現することは出来ませんでした。Kurt翔泳社Amazonマクロについてはこちらをどうぞ。前回、作ってみたFunctorのfmapはコンテナもしくはコンテキスト内の値に対して、関数を適応することが可能でした。b最も身近なfmapがリストに対して定義されていました。それはmap関数です。fmapとmap関数の定義を見比べてみれば明らかでした。[b]Functorの制約⛓しかしFunctorはある制約を抱えています。それは適応する関数の引数が1つの場合にしか対応出来ないという点です。受け取った数値に対して、m分だけ加算をする単純なaddNという関数を定義したので、この関数をfmapを使って、1から10の値を持つリストに適応してみます。この関数は引数を2つ受け取る必要があることに注目です。10]すると以下のようなエラーが発生するためコンパイルをすることが出来ません。b)となっているので当然ですが、引数が1つの関数以外は受け付けません。引数が1つの関数として定義してあげれば、このエラーは発生しなくなります。[11,12,13,14,15,16,17,18,19,20]この制約はHaskellだけのものではありません。ElixirのEnum.map関数でも、同じ制約がなされています。3の場合があるようですが、任意の値を渡すことが出来ないという視点では同様の制約があると判断出来ます。10);hexdocs.pmdeveloper.mozilla.orgApplicativeの登場このFunctorの制約をクリアすることが出来るのがApplicativeというクラス(型)です。Applicativeはコンテナ、コンテキスト内(モナドのインスタンス)で部分適応された関数を適応させることが出来ます。hackage.haskell.org部分適応について知りたい方は過去の記事を読んでみてください。www.okb-shelf.workApplicativeには<*>(appと読むそう)という関数が定義されています。この関数の型の定義は以下のようになっています。bFunctorのfmapにかなり似ていますね。b異なるのが、第1引数です。b))を引数に受け取ります。コンテナ、コンテキスト内にある関数を引数に受け取れるというのは、コンテナ、コンテキスト内で関数を部分適応した場合に、その関数をそのまま受け取って、要素に対して適応が出来るということです。いざApplicative言葉で説明、理解するのは難しいので例を見ていきます。適応させる関数は先ほど挫折した、addN関数です。この関数は2つの引数を受け取ります。m以下が先ほどエラーとなった記述です。10]ここにapp(<*>)を付け加えます。10)これでエラーは発生せず、思った通りの値が返ってくるのですが、何やら見慣れない状態になってしまいました。順に値を追っていけば、そう難しいものではありません。10]が返す値について考えてみます。bより、返ってくる値はコンテナ、コンテキスト内に存在するbの値です。Haskellではデフォルトで全ての関数が部分適応されるので、addNに対して、第1引数だけを受け渡して部分適応された関数がコンテナ、コンテキスト内でbとして返っています。10]という呼び出しでエラーになっていないことからも明確です。a]現在、コンテナ、コンテキスト内に部分適応された関数が返っている状態です。コンテナ、コンテキスト内の関数を引数に受け取り、処理を適応するのはapp(<*>)の役目です。10)先程の定義通りになりました。最後にpureという構文について簡単に説明しておきます。Applicativeにはappの他にpureが定義されています。a定義を見てわかるように、受け取った値をApplicativeのクラス(型)にするというだけのシンプルな関数です。要するにコンテナ、コンテキスト外の値をコンテナ、コンテキスト内に引きずり込むための関数です。addNの第2引数に渡したい10はそのままだとInt型の値なので、appの戻り値エラーになってしまいます。10appとpureという2つの関数を定義することでApplicativeは2つ以上の引数を持つ関数への処理の適応を可能とします。いざElixirで作ってみる🧪...。最初に記述した通り、Elixirでは言語の制約上、HaskellでのApplicativeと同等のものを作るのは非常に難しいです。Haskellはデフォルトで関数を部分適応するからです。HaskellのApplicativeが強力なのは、この仕様があるからこそです。デフォルトで部分適応がされない以上、自分で部分適応がされるようにカリー化した関数を定義するか、マクロを作成する他、ありません。今回は簡単のため、カリー化した関数を用いました。引数2つの状態のままの関数では再現が出来ませんでした。endendApplicativeのappとpureに関してはHaskellに定義されていた引数に近しい状態に出来ました。適応させる関数は先程も登場したaddNですが、上記にもあるようにカリー化することでしか再現することが出来なかったので、元々、引数2つで定義していたものをカリー化して引数1つの関数を返すようにしています。end実行してみます。20]重要なのは、Enum.mapの中で、Func.addN(n)を呼び出した後の戻り値です。以下のように出力されます。この時点で、カリー化した関数が返り部分適応されていることが分かります。Func.addN/1>]そして、appによって部分適応された関数を適応するだけです。pureについては簡単のため受け取った値を要素に含めたリスト返すようにしました。実行結果は自明だと思うのです、割愛します。まとめ📖ApplicativeはFunctorの制約をクリアするために非常に便利なクラス(型)です。Functorのfmapでは引数が1つの関数のみしか適応することが出来ませんが、Applicativeのappは、コンテナ、コンテキスト内にある関数に対しても処理を適応することが出来るので、Functorをより強力にしたクラス(型)と言えます。ApplicativeはHaskellの言語仕様である全ての関数がデフォルトで部分適応されるからこそ、強力です。他言語でApplicativeを再現しようとしても、デフォルトと部分適応されない以上は同等の力を引き出すことは難しく、個別にカリー化した関数などを定義する必要があります。次は大本命のMonadです。「入門Haskellプログラミング」を読み進め...2021-09-09【JavaScriptで解説】部分適用って何?カリー化との違いは?部分適用とは何か。混同されるカリー化という手法とは...2021-08-08【感想・まとめ】「関数型プログラミングなんもわからん。考えよう」に参加してきました。「関数型プログラミングなんもわからん。22:23名古屋発のベンチャーでWEBアプリのバックエンドをメインに作っています。エンジニア育成やチームビルディングにもちょっとだけ携わっています。3はてなブログをはじめよう!takamizawa46さんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。