7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LIFULLAdvent Calendar 2023

Day 19

Haskellちょっと勉強したのでモナドについてまとめてみた

Last updated at Posted at 2023-12-22

はじめに

最近Haskellをちょっと勉強してみたので、モナドについて解説してみた記事です。

モナドって何?

Haskellはいわゆる「純粋関数型言語」で、IOや状態を変更するなど「副作用」のある処理を許さないという特徴があります。
そのおかげで参照透過性を高められることができるのですが、一般的な手続き型言語で使われるような処理が使わず不便なので、「モナド」と言われる処理を使うことでこの問題を解決しています。

モナドを使った例

まず簡単なモナドの例を、Maybeと呼ばれるモナドを使って説明してみます。
このモナドは、RustでいうOptionのようなもので、返り値をJustNoneの型に包むことでnullableな値のハンドリングをしやすくしています。

例えばこのような感じです。

safeDivide :: Float -> Float -> Maybe Float
safeDivide _ 0 = Nothing
safeDivide x y = Just (x / y)

safeRoot :: Float -> Maybe Float
safeRoot x
    | x < 0     = Nothing
    | otherwise = Just (sqrt x)

safeOperation :: Float -> Float -> Maybe Float
safeOperation x y = safeDivide x y >>= \quotient -> safeRoot quotient

これを使うと、たとえば

ghci> safeOperation 4 2
Just 1.4142135
ghci> safeOperation 4 0
Nothing
ghci> safeOperation (-4) 2
Nothing

のように表現できます。

ここで重要なのは>>=演算子で、これはsafeDivide x yの計算結果を\quotient -> safeRoot quotientにbindしています。つまり、

Maybe a型の値と a -> Maybe b 型の関数がある場合、>>= 演算子を使用してこの関数を Maybe a 型の値に適用し、Maybe b 型の新しい値を得る。

という動作をしていることになります。

>>=を使うとbindを数珠繋ぎにできるのですが、数珠繋ぎが増えた場合可読性が落ちてしまいます。そのため、Haskellではdo構文が用意されています。これを使うと、モナドの処理を手続型のように書くことができます。

これを使って、safeOperationを書き直してみましょう。

safeOperation :: Float -> Float -> Maybe Float
safeOperation x y = do
    quotient <- safeDivide x y
    safeRoot quotient

ここで、bind演算子は<-を示しています。手続型のように、わかりやすくなったことが見て取れると思います。

モナドを作ってみる

では、Maybeモナドを例に挙げて、モナドを自分で作ってみましょう。Haskellには型クラスと呼ばれるinterfaceの様なものが存在しているので、これを使ってモナドを作ることができます。

モナドの型クラスは以下の通りです。

class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  return :: a -> m a

  (>>) :: m a -> m b -> m b
  m >> n = m >>= \_ ->  n

  fail :: String -> m a
  fail = error

ここで重要なのは(>>=)returnで、(>>=)がbind演算子を表しています。((>>)failの説明は重要ではないので省略します。)
returnは値をモナドに包むための処理です。これを使うと、以下の様なことができます。

-- 値を Maybe コンテキストに入れる
example :: Maybe Int
example = return 5

これを実際に実装してみましょう。

instance Monad Maybe where
  Nothing >>= f = Nothing
  Just x  >>= f = f x
  return x = Just x
  fail _ = Nothing

これをみると分かる通り、NothingをbindするとNothingを返し、Just xをbindするとモナドの中身を関数の引数に渡すことができます。
また、returnを使うと値をモナドの型に包んでいることがわかりますね。

おわりに

駆け足でモナドの解説をしてみたのですがいかがでしょうか?
かなり端折っているので説明不足なところも多いのですが、なんとなくの感覚は掴めるのではないかと思います。

またモナドにはIOやReader, Writer, State等色々な種類があり、それぞれで違う役割を持っているのでそれぞれ調べてみると面白いと思います。(なんでもできるFreeモナドというものもあるそうなのですが、自分はよくわかっていません)

参考

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?