您确实为所有内容提供了返回类型Reader Env a
,尽管这并不像您想象的那么糟糕。一切都需要这个标签的原因是,如果f
取决于环境:
type Env = Int
f :: Int -> Reader Int Int
f x = do
env <- ask
return (x + env)
并g
调用f
:
g x = do
y <- f x
return (x + y)
然后g
还取决于环境 - 行中绑定的值y <- f x
可能不同,具体取决于传入的环境,因此适当的类型g
是
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
monad 差。当您想要扩展语义时,困难就来了。假设您还依赖于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
和的旧定义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)
他们现在完美地工作。比较两种方法: