Dav*_*vid 3 monads haskell 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);
Run Code Online (Sandbox Code Playgroud)
'ask'如何获取字符串参数?这是我的理解,因为类型声明
calculateContentLength :: Reader String Int
Run Code Online (Sandbox Code Playgroud)
函数'calculateContentLength'有一个返回类型(类型为Reader String Int),但它没有传入的参数.我意识到函数本身只是传递给runReader函数的两个参数之一,但是runReader的第二个参数's'与'calculateContentLength'中的'ask'有什么关联?
换句话说,'calculateContentLength'如何"知道"关于(并获取访问)传递给'runReader'的第二个参数?
让我们看一下定义的方法Reader.
newtype Reader r a = Reader { runReader :: r -> a }
Run Code Online (Sandbox Code Playgroud)
这Reader是一个带函数的构造函数.该函数采用类型的环境r,并返回类型的结果a.
ask = Reader { runReader = \env -> env }
ask = Reader id
Run Code Online (Sandbox Code Playgroud)
该return操作只是忽略环境并返回一个值.
return x = Reader { runReader = \_ -> x }
Run Code Online (Sandbox Code Playgroud)
该m >>= n操作执行简单的排序:它占用环境,m在该环境中运行,然后n在相同的环境中运行,并将结果传递给它m.
m >>= n = Reader $ \env -> let
a = runReader m env
in runReader (n a) env
Run Code Online (Sandbox Code Playgroud)
所以现在我们可以举出你的例子,去除它,并逐步减少它.
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
Run Code Online (Sandbox Code Playgroud)
现在应该更容易看到如何runReader calculateContentLength与刚刚相同length,以及如何ask不神奇 - monad的>>=操作构建了一个函数,当您运行计算时,它隐式地为您传递环境runReader.
实际上,它Reader是用术语定义的ReaderT,它使用monadic动作而不是纯函数,但它的实现形式基本相同.