我知道如何使用它,但我不明白它是如何做到的(Reader monad)

时间:2014-08-24 20:51:50

标签: haskell monads reader-monad

考虑以下代码(省略明显的部分)

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函数的两个参数之一,但是runReader的第二个参数's'与'calculateContentLength'中的'ask'有什么关联?

换句话说,'calculateContentLength'如何“知道”关于(并获取访问)传递给'runReader'的第二个参数?

1 个答案:

答案 0 :(得分:9)

让我们看一下定义Reader的方法。

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

所以Reader是一个带函数的构造函数。该函数采用r类型的环境,并返回类型a的结果。

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

return操作只是忽略环境并返回一个值。

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

m >>= n操作执行简单的排序:它接管环境,在该环境中运行m,然后在同一环境中运行n,并将结果传递给{{1} }。

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如何不神奇 - monad的ask操作构建了一个函数当您使用>>=运行计算时,隐式地为您传递环境。

实际上,runReader是用Reader来定义的,ReaderT使用monadic动作而不是纯函数,但它的实现形式基本相同。