考虑以下代码(省略明显的部分)
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'的第二个参数?
答案 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动作而不是纯函数,但它的实现形式基本相同。