考虑以下代码(省略了明显的部分)
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);
“问”如何获取字符串参数?据我了解,由于类型声明
calculateContentLength :: Reader String Int
函数“ calculateContentLength”具有返回类型(类型为Reader String Int),但没有传入参数。我意识到该函数本身只是传递给runReader函数的两个参数之一,但是runReader的第二个参数“ s”与“ calculateContentLength”内部的“ ask”有何关系?
换句话说,“ calculateContentLength”如何“知道”(并访问)通过“ runReader”传递的第二个参数?
最佳答案
让我们看看定义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
,将其传递给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
的不可思议性-monad的>>=
操作构建了一个函数,当您运行该函数时会隐式传递环境runReader
计算。实际上,
Reader
是用ReaderT
定义的,它使用单调操作而不是纯函数,但是其实现形式基本相同。