Haskell Language
モナド
サーチ…
前書き
モナドは、構成可能なアクションのデータ型です。 Monad
は、値がそのようなアクションを表す型コンストラクタのクラスです。おそらく、 IO
最も有名なものです:の値IO a
「取得するためのレシピです現実の世界から値を」。 a
私たちは、型のコンストラクタm
( []
やMaybe
) は、アクションの構成に関するある種の法律を満たすinstance Monad m
がある場合、モナドを形成すると言っていinstance Monad m
。 ma
、私たちは、「結果がタイプa
持つアクション」としてma
することができます。
多分モナド
Maybe
に似て-おそらく空の値を表すために使用されるnull
他の言語では。通常、何らかの方法で失敗する可能性のある出力関数の型として使用されます。
以下の関数を考えてみましょう:
halve :: Int -> Maybe Int
halve x
| even x = Just (x `div` 2)
| odd x = Nothing
整数を半分にしようとするInt
に応じて、 halve
を1つのアクションとして考えると、それが奇妙であれば失敗します。
どうやってんhalve
整数3回?
takeOneEighth :: Int -> Maybe Int -- (after you read the 'do' sub-section:)
takeOneEighth x =
case halve x of -- do {
Nothing -> Nothing
Just oneHalf -> -- oneHalf <- halve x
case halve oneHalf of
Nothing -> Nothing
Just oneQuarter -> -- oneQuarter <- halve oneHalf
case halve oneQuarter of
Nothing -> Nothing -- oneEighth <- halve oneQuarter
Just oneEighth ->
Just oneEighth -- return oneEighth }
-
takeOneEighth
は、一緒にチェーンされた3つのhalve
ステップのシーケンスです。 -
halve
ステップが失敗した場合は、全体の構成が失敗するようにtakeOneEighth
を設定します。 -
halve
ステップが成功すれば、結果を先取りしたい。
instance Monad Maybe where
-- (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= f = Nothing -- infixl 1 >>=
Just x >>= f = Just (f x) -- also, f =<< m = m >>= f
-- return :: a -> Maybe a
return x = Just x
今私達は書くことができます:
takeOneEighth :: Int -> Maybe Int
takeOneEighth x = halve x >>= halve >>= halve -- or,
-- return x >>= halve >>= halve >>= halve -- which is parsed as
-- (((return x) >>= halve) >>= halve) >>= halve -- which can also be written as
-- (halve =<<) . (halve =<<) . (halve =<<) $ return x -- or, equivalently, as
-- halve <=< halve <=< halve $ x
Kleisliの構成 <=<
は、 (g <=< f) x = g =<< fx
、または(f >=> g) x = fx >>= g
として等価的に定義されます。それによって、上記の定義はちょうど
takeOneEighth :: Int -> Maybe Int
takeOneEighth = halve <=< halve <=< halve -- infixr 1 <=<
-- or, equivalently,
-- halve >=> halve >=> halve -- infixr 1 >=>
すべてのモナドが従うべきである3つのモナドの法則がありますが、それはのインスタンスであるすべてのタイプであるMonad
型クラス:
1. return x >>= f = f x
2. m >>= return = m
3. (m >>= g) >>= h = m >>= (\y -> g y >>= h)
m
はモナド、 f
はa -> mb
、 g
はb -> mc
型です。
あるいは、上で定義した>=>
Kleisli合成演算子を使用して、
1. return >=> g = g -- do { y <- return x ; g y } == g x
2. f >=> return = f -- do { y <- f x ; return y } == f x
3. (f >=> g) >=> h = f >=> (g >=> h) -- do { z <- do { y <- f x; g y } ; h z }
-- == do { y <- f x ; do { z <- g y; h z } }
これらの法則に従うことは、モナド関数を使用してそれらを合成することが、他のモナドと同様に合理的な方法で動作することを保証するので、モナドについて理由を考えるのがより簡単になります。
Maybe
モナドが3つのモナドの法則に従っているMaybe
どうかを調べましょう。
- 左のアイデンティティ法則 -
return x >>= f = fx
return z >>= f
= (Just z) >>= f
= f z
- 正しいアイデンティティ法 -
m >>= return = m
-
Just
データコンストラクタ
Just z >>= return
= return z
= Just z
-
Nothing
データコンストラクタ
Nothing >>= return
= Nothing
- 連想法則 -
(m >>= f) >>= g = m >>= (\x -> fx >>= g)
-
Just
データコンストラクタ
-- Left-hand side
((Just z) >>= f) >>= g
= f z >>= g
-- Right-hand side
(Just z) >>= (\x -> f x >>= g)
(\x -> f x >>= g) z
= f z >>= g
-
Nothing
データコンストラクタ
-- Left-hand side
(Nothing >>= f) >>= g
= Nothing >>= g
= Nothing
-- Right-hand side
Nothing >>= (\x -> f x >>= g)
= Nothing
IOモナド
型の値を取得する方法はありませんa
型の式のうち、 IO a
、そこにあってはなりません。実際には、なぜモナドを使ってIO
をモデル化するのかは、これが大きな部分を占めています。
タイプIO a
の表現は、現実の世界と対話できるアクションを表すものと考えることができ、実行されるとタイプa
ものにa
ます。たとえば、関数getLine :: IO String
からのgetLine :: IO String
は、 getLine
下に抽出できる特定の文字列があることを意味するわけではありません。つまり、 getLine
は標準入力から行を取得する動作を表します。
驚くべきことではありませんが、 main :: IO ()
はHaskellプログラムが実世界と対話する計算/アクションを表しているためです。
IO
がモナドでIO a
ため、 IO
型の式にできること :
最初のアクションを実行する新しいアクションを生成するために
(>>)
を使用して2つのアクションをシーケンスし、それが生成した値を破棄してから、2番目のアクションを実行します。-- print the lines "Hello" then "World" to stdout putStrLn "Hello" >> putStrLn "World"
時には、最初のアクションで生成された値を破棄したくない場合もあります。実際には2番目のアクションにフィードしたいと思うことがあります。そのために、
>>=
ます。IO
場合、タイプ(>>=) :: IO a -> (a -> IO b) -> IO b
です。-- get a line from stdin and print it back out getLine >>= putStrLn
通常の値をとり、それをあなたが与えた値をただちに返すアクションに変換します。この関数は、
do
notationの使用を開始するまで、あまり明らかに有用ではありdo
。-- make an action that just returns 5 return 5
ここのIOモナドのHaskell Wikiから
リストモナド
リストはモナドを形成する。彼らはこれに相当するモナドのインスタンス化を持っています:
instance Monad [] where return x = [x] xs >>= f = concat (map f xs)
計算では非決定論をエミュレートするためにそれらを使用できます。 xs >>= f
を使うと、関数f :: a -> [b]
はリストxs
上にマップされ、 f
の各アプリケーションの結果のリストをxs
各要素に渡ってリストし、結果はすべての結果の1つのリストに連結されます。たとえば、 do-notationを使用して 2つの非決定論的な数の和を計算します 。その合計は、2つのリストからのすべての整数対の合計のリストで表され、各リストは非決定的な数のすべての可能な値を表します。
sumnd xs ys = do
x <- xs
y <- ys
return (x + y)
または同等に、 Control.Monad
liftM2
を使用しliftM2
。
sumnd = liftM2 (+)
私達は手に入れました:
> sumnd [1,2,3] [0,10]
[1,11,2,12,3,13]
モナドは応用のサブクラスとして
GHC 7.10のように、 Applicative
のスーパークラスであるMonad
(すなわち、あるすべてのタイプMonad
またなければならないApplicative
)。 Applicative
( pure
、 <*>
)のすべてのメソッドは、 Monad
のメソッド( return
、 >>=
)で実装できます。
ことは明らかであるpure
とreturn
、同等の目的を果たすので、 pure = return
。 <*>
の定義はあまりにも明確です。
mf <*> mx = do { f <- mf; x <- mx; return (f x) }
-- = mf >>= (\f -> mx >>= (\x -> return (f x)))
-- = [r | f <- mf, x <- mx, r <- return (f x)] -- with MonadComprehensions
-- = [f x | f <- mf, x <- mx]
この関数は、標準ライブラリのap
として定義されています。
したがって、あるタイプのMonad
インスタンスをすでに定義している場合、それを定義することによって、 "無料"のためのApplicative
インスタンスを効果的に得ることができます
instance Applicative < type > where
pure = return
(<*>) = ap
モナド法と同様に、これらの同値性は強制されませんが、開発者はそれらが常に維持されていることを保証する必要があります。
モナド計算から値を抽出する一般的な方法はありません
値をアクションにラップし、ある計算の結果を別の計算にパイプすることができます。
return :: Monad m => a -> m a
(>>=) :: Monad m => m a -> (a -> m b) -> m b
しかし、Monadの定義は、 Monad m => ma -> a
の型の関数の存在を保証するものではありません。
つまり、一般的に計算から値を抽出する方法はありません (つまり、その値を "unwrap"します)。多くの場合、これが当てはまります。
extract :: Maybe a -> a
extract (Just x) = x -- Sure, this works, but...
extract Nothing = undefined -- We can’t extract a value from failure.
具体的には、関数IO a -> a
は存在せず、これはしばしば初心者を混乱させる。 この例を参照してください。
do-notation
do
-notationは、モナドの構文的砂糖です。ここにルールがあります:
do x <- mx do x <- mx y <- my is equivalent to do y <- my ... ...
do let a = b let a = b in ... is equivalent to do ...
do m m >> ( e is equivalent to e)
do x <- m m >>= (\x -> e is equivalent to e)
do m is equivalent to m
たとえば、これらの定義は同等です。
example :: IO Integer
example =
putStrLn "What's your name?" >> (
getLine >>= (\name ->
putStrLn ("Hello, " ++ name ++ ".") >> (
putStrLn "What should we return?" >> (
getLine >>= (\line ->
let n = (read line :: Integer) in
return (n + n))))))
example :: IO Integer
example = do
putStrLn "What's your name?"
name <- getLine
putStrLn ("Hello, " ++ name ++ ".")
putStrLn "What should we return?"
line <- getLine
let n = (read line :: Integer)
return (n + n)
モナドの定義
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
モナドを扱うための最も重要な関数は、 バインド演算子>>=
です。
(>>=) :: m a -> (a -> m b) -> m b
- 考える
ma
「とアクションとして結果」。a
- 考える
a -> mb
「(に応じて、アクションなどでパラメータ)a
b
。結果」。
>>=
最初のアクションの結果を2番目のアクションにパイプすることで、2つのアクションを一緒に処理します。
Monad
によって定義された他の関数は次のとおりです。
return :: a -> m a
その名前は残念です。このreturn
は、命令的プログラミング言語に見られるreturn
キーワードとは関係ありません。
return x
は結果としてx
を生成する簡単なアクションです。 (それは次の意味では些細なことです:)
return x >>= f ≡ f x -- “left identity” monad law
x >>= return ≡ x -- “right identity” monad law