4

次のコードを検討してください (明らかな部分は省略しています)。

main = do

    let s = "123456";
    let len = runReader calculateContentLength s
    putStrLn $ "Original 's' length: " ++ (show len)


calculateContentLength :: Reader String Int
calculateContentLength = do
    content <- ask           -- this seems to be the same as 'reader id'
    return (length content);

「ask」はどのように文字列パラメーターを取得しますか? 型宣言のため、それは私の理解です

calculateContentLength :: Reader String Int

関数 'calculateContentLength' には戻り値の型 (Reader String Int 型) がありますが、入力引数はありません。関数自体は、runReader 関数に渡される 2 つの引数の 1 つにすぎないことはわかっていますが、runReader の 2 番目のパラメーターである「s」は、「calculateContentLength」内の「ask」にどのように結び付けられるのでしょうか?

つまり、「calculateContentLength」は、「ru​​nReader」で渡された 2 番目の引数についてどのように「認識」(およびアクセス) するのでしょうか?

4

1 に答える 1

12

を定義する 1 つの方法を見てみましょうReader

newtype Reader r a = Reader { runReader :: r -> a }

関数Readerを取るコンストラクタも同様です。その関数は type の環境を取り、 typerの結果を返しますa

ask = Reader { runReader = \env -> env }
ask = Reader id

このreturn操作は、環境を無視して値を返すだけです。

return x = Reader { runReader = \_ -> x }

このm >>= n操作は単純な順序付けを行います。環境を取得mし、その環境で実行nしてから、同じ環境で実行し、 の結果を渡しますm

m >>= n = Reader $ \env -> let
  a = runReader m env
  in runReader (n a) env

これで、あなたの例を取り上げて、脱糖し、段階的に減らすことができます.

calculateContentLength = do
  content <- ask
  return (length content)

-- substitute definition of 'ask'

calculateContentLength = do
  content <- Reader id
  return (length content)

-- substitute definition of 'return'

calculateContentLength = do
  content <- Reader id
  Reader (\_ -> length content)

-- desugar 'do' into '>>='

calculateContentLength =
  Reader id >>= \content -> Reader (\_ -> length content)

-- definition of '>>='

calculateContentLength = Reader $ \env -> let
  a = runReader (Reader id) env
  in runReader ((\content -> Reader (\_ -> length content)) a) env

-- reduce lambda

calculateContentLength = Reader $ \env -> let
  a = runReader (Reader id) env
  in runReader (Reader (\_ -> length a)) env

-- definition of 'runReader'

calculateContentLength = Reader $ \env -> let
  a = id env
  in runReader (Reader (\_ -> length a)) env

-- definition of 'id'

calculateContentLength = Reader $ \env -> let
  a = env
  in runReader (Reader (\_ -> length a)) env

-- remove redundant variable

calculateContentLength = Reader $ \env
  -> runReader (Reader (\_ -> length env)) env

-- definition of 'runReader'

calculateContentLength = Reader $ \env -> (\_ -> length env) env

-- reduce

calculateContentLength = Reader $ \env -> (length env)
calculateContentLength = Reader length

runReader calculateContentLengthこれで、 が単に と同じlengthであり、が魔法ではないことが簡単にわかるはずですask— モナドの>>=操作は、 で計算を実行するときに暗黙的に環境を渡す関数を構築しますrunReader

実際にReaderは、 は純粋な関数の代わりにモナド アクションを使用する で定義されますReaderTが、その実装の形式は本質的に同じです。

于 2014-08-24T21:15:11.697 に答える