You do给所有东西返回类型Reader Env a
,尽管这并不像你想象的那么糟糕。一切都需要这个标签的原因是如果f
取决于环境:
type Env = Int
f :: Int -> Reader Int Int
f x = do
env <- ask
return (x + env)
and g
calls f
:
g x = do
y <- f x
return (x + y)
then g
还取决于环境 - 行中绑定的值y <- f x
可能会有所不同,具体取决于传入的环境,因此适当的类型g
is
g :: Int -> Reader Int Int
这其实是一件好事!类型系统迫使您明确识别函数依赖于全局环境的位置。您可以通过定义短语的快捷方式来减轻打字的痛苦Reader Int
:
type Global = Reader Int
现在你的类型注释是:
f, g :: Int -> Global Int
这更具可读性。
另一种方法是将环境显式传递给所有函数:
f :: Env -> Int -> Int
f env x = x + env
g :: Env -> Int -> Int
g x = x + (f env x)
这是可行的,事实上,从语法角度来看,它并不比使用Reader
单子。当你想要扩展语义时,困难就来了。假设您还依赖于类型的可更新状态Int
其中计算函数应用程序。现在您必须将您的功能更改为:
type Counter = Int
f :: Env -> Counter -> Int -> (Int, Counter)
f env counter x = (x + env, counter + 1)
g :: Env -> Counter -> Int -> (Int, Counter)
g env counter x = let (y, newcounter) = f env counter x
in (x + y, newcounter + 1)
这显然不太令人愉快。另一方面,如果我们采用单子方法,我们只需重新定义
type Global = ReaderT Env (State Counter)
的旧定义f
and g
继续工作,没有任何麻烦。要更新它们以具有应用程序计数语义,我们只需将它们更改为
f :: Int -> Global Int
f x = do
modify (+1)
env <- ask
return (x + env)
g :: Int -> Global Int
g x = do
modify(+1)
y <- f x
return (x + y)
现在它们工作得很好。比较两种方法: