前回までのあらすじ📖 「入門Haskellプログラミング」を読み進めながら、理解を深めるためにElixirを使ってHaskellのFunctorとApplicativeを再現してみました。
[https://www.okb-shelf.work/entry/original_functor:embed:cite]
[https://www.okb-shelf.work/entry/original_applicative:embed:cite]
Functorは上手く再現出来ましたが、Applicativeはデフォルトで部分適応されるというHaskellの仕様がなければ、完全再現することが出来ず、カリー化した関数で近しいものを再現しました。
FunctorとApplicativeを用いることでがコンテナ、コンテキスト内の値(例: リスト)に対して以下の操作をすることが出来ます。
引数1つの関数をコンテナ、コンテキスト内の値に対して適応する(Functor : fmap) コンテナ、コンテキスト内の部分適応された関数をコンテナ、コンテキスト内の値に対して適応する(Applicative : app) 引数2つ以上の関数を適応することが出来る コンテナ、コンテキスト外の値をコンテナ、コンテキスト内に格納する(Applicative : pure) さて、これだけの操作がコンテナ、コンテキスト内に対して可能になりましたが、現状ではまだ出来ないことがあります。その問題を解決するためにMonadが必要なのです。
Monadとは🤔 HaskellではMonadはFunctorやApplicativeと同様にクラス(型)として定義されており、関数を定義しています。
よく言われるカタカナ表記の「モナド」とはHaskellにおいてはMonadクラス(型)のインスタンスに持つクラス(型)の総称です。試しにMaybeクラス(型)についての情報を見てみると、Monadをインスタンスに持つ事が分かります。Maybeクラス(型)はモナドの1種です。
*Main> :info Maybe type Maybe :: * -> * data Maybe a = Nothing | Just a -- Defined in ‘GHC.Maybe’ instance Applicative Maybe -- Defined in ‘GHC.Base’ instance Eq a => Eq (Maybe a) -- Defined in ‘GHC.Maybe’ instance Functor Maybe -- Defined in ‘GHC....
概要📖 Haskellではエラーが発生した際に対応するためにEitherというモナドを使う EitherはLeft(エラー時)とRight(正常時)の2つの値を持ち、それぞれ別の型の値を受け取ることが可能(Either = Left a | Right b) LeftにはStringがよく用いられ、エラーメッセージを格納することが多い Eitherが必要なのは呼び出し元に、なぜエラーになったのかを伝えるため Maybeではエラー、値なしの理由を呼び出しもとに伝えることが出来ない Elixirで実装するにはTupleとAtomを使うのが良さげ 前回までのあらすじ📖 「入門Haskellプログラミング」を読み進めながら、理解を深めるためにElixirを使ってHaskellのFunctorとApplicative, Monadを実装しました。
[asin:B07SFCMP66:detail]
以上の3つで「入門Haskellプログラミング」のUNIT5で扱われていたコンテキストでの型の操作に関しての項目が終了しました。
いやー、長かった!抽象的な概念が続いたので理解するのにかなり苦労しましたが、何とかUNIT5を終了することが出来ました。
これでコンテキスト内に存在する値に対しての操作(関数適応)が可能となりました🎉
最後のモナド🔥 本書で紹介されているモナドはあと1つです。
Eitherと呼ばれるエラーを扱うためのモナドで、呼び出し元になぜエラーになったのかを伝えるために利用されています。 (The Either type is sometimes used to represent a value which is either correct or an errorより判断)
The Either type represents values with two possibilities: a value of type Either a b is either Left a or Right b.
The Either type is sometimes used to represent a value which is either correct or an error; by convention, the Left constructor is used to hold an error value and the Right constructor is used to hold a correct value (mnemonic: “right” also means “correct”)....
Hugoの形式で貼り付け tweet はてぶで書いた記事が全くGoogleに登録されないので、別のサービスに乗り換えようかな。
— OKB (@sing_mascle69) November 7, 2021 ref adsense
リンク
この記事の概要📖 関数型言語の多くでは値は束縛されるため、不変である 値を再束縛(≒再代入)することが出来ないため、値を更新する際には新たなデータを作る必要がある 更新の度に新たなデータを作ることで、関数型言語でもカプセル化をすることが可能になる 関数型言語では値は不変⏳ 多くの関数型言語では値は不変であり、一度、宣言した変数に束縛されている値を変更することが出来ません。
printName :: IO () printName = print name where name = "okb" name = "okb2" コンパイルしてみると…
*Main> :l sample.hs [1 of 1] Compiling Main ( sample.hs, interpreted ) sample.hs:5:9: error: Conflicting definitions for ‘name’ Bound at: sample.hs:5:9-12 sample.hs:6:9-12 | 5 | where name = "okb" | ^^^^^^^^^^^^... Failed, no modules loaded. Conflicting definitions for ‘name’(nameという定義が衝突している)というエラーが出て、コンパイルに失敗してしまいます。
この制限はリストやオブジェクト、マップであっても同じです。例えばリストのN番目だけを更新するということは基本的には出来ず、N番目の要素が変化した新たなリストを作り直す必要があります。
しかしRubyであれば同じ処理のコードが問題なく実行されます。Rubyは再代入(再束縛)を許可しているからです。 他の再代入を許可している言語でも同じ結果が得られます。
name = "okb" name = "okb2" print name # okb2 リストの更新...