data T b = E | N b (T b) (T b)
f :: T b -> Reader Int (T Int)
f (N i l r) = ask >>= \x -> local ((-)4) (f l) >>= \l' -> local ((-)1) (f r) >>= \r' -> return (N x l' r')
f E = return E
我在理解此代码的工作原理时遇到了问题。特别是,ask
如何知道环境的位置(在我们的例子中只是Int
)?
更确切地说:我是一位势在必行的程序员,而且在这种语言中我很容易。可以在任何对象上调用方法,例如:obj.f()
,或者当我们希望函数使用外部数据时,我们必须通过参数传递数据。
答案 0 :(得分:4)
读者monad 所做的那种;它为您提供了ask
功能,其中包括#34;魔法"凭空捏造价值。要实际使用,您需要调用runReader
,并为其提供Int
环境。然后,Reader
类型会自动将runReader
次呼叫传播到每个ask
来电。
答案 1 :(得分:2)
简短,手工波浪的答案。 Reader Int (T Int)
值基本上只是Int -> (T Int)
类型的包装函数。为了运行该功能,我们首先需要提取它。我们使用runReader
。
data T b = ... deriving (Show)
main = let tree = (N 10 (N 8 E E) E)
g = f tree
h = runReader g
in print $ h 20
(通常,您只需编写print $ runReader (f tree) 20
;我将其拆分以对应下面的粗略类比。)
ask
(由MonadReader
类型类定义,并由用于定义ReaderT
类型的Reader
monad转换器实现,实质上检索传递给的参数的值h
。
在某种意义上,Reader Int (T Int)
是一个包含调用函数g
的函数ask
的对象。 runReader g
创建一个新函数,在调用时,定义一个只返回其参数的函数ask
,然后调用g
。现在,当g
调用ask
时,它会返回最初传递给h
的参数。