サーチ…


前書き

モナドは、構成可能なアクションのデータ型です。 Monadは、値がそのようなアクションを表す型コンストラクタのクラスです。おそらく、 IO最も有名なものです:の値IO a 「取得するためのレシピです現実の世界から値を」。 a

私たちは、型のコンストラクタm[]Maybeは、アクションの構成に関するある種の法律を満たすinstance Monad mがある場合、モナドを形成すると言っていinstance Monad mma 、私たちは、「結果がタイプ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はモナド、 fa -> mbgb -> 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どうかを調べましょう。

  1. 左のアイデンティティ法則 - return x >>= f = fx
return z >>= f 
= (Just z) >>= f 
= f z
  1. 正しいアイデンティティ法 - m >>= return = m
  • Justデータコンストラクタ
Just z >>= return
= return z
= Just z  
  • Nothingデータコンストラクタ
Nothing >>= return
= Nothing 
  1. 連想法則 - (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 )。 Applicativepure<*> )のすべてのメソッドは、 Monadのメソッド( return>>= )で実装できます。

ことは明らかであるpurereturn 、同等の目的を果たすので、 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


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow