让我们看一种定义Reader
.
newtype Reader r a = Reader { runReader :: r -> a }
Reader
接受函数的构造函数也是如此。该函数采用 type 的环境r
,并返回 type 的结果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
与 just 有何相同length
,以及如何ask
不神奇——monad 的>>=
操作构建了一个函数,当您使用 运行计算时,该函数会为您隐式传递环境runReader
。
实际上,Reader
是用 定义的ReaderT
,它使用单子动作而不是纯函数,但其实现形式基本相同。