我认为您基本上对Reader 的定义及其使用方式感到困惑。
让我们快速构建我们自己的:
newtype MyReader env a = MyReader (env -> a)
没错,Reader 可以只是一个 函数,它接受当前环境并返回一些 a - 这里在 newtype 包装器中,因此我们可以在其上定义一些实例而无需太多扩展
runReader 非常简单——它只是将给定的环境应用到我们包装的函数中:
runReader :: MyReader env a -> env -> a
runReader (MyReader f) = f
这可能应该回答你的大部分问题 - runReader 在你的例子中不会调用 tom - 你将它作为参数提供,所以 你 用 tom 调用包装函数;)
但是让我们完成我们的 Monad
所以让我们先把它变成一个仿函数 - GHC 现在可以推导出它,但我想理解手工操作会更好:
instance Functor (MyReader env) where
fmap f (MyReader rdr) = MyReader (f . rdr)
如果您仔细观察,您会发现这确实只是函数组合 - 所以映射的阅读器将采用 env,将包装的函数应用到它,然后在 f 到这个结果。
应用是下一个:
instance Applicative (MyReader env) where
pure a = MyReader (\_ -> a)
(MyReader f_a_to_b) <*> (MyReader f_a) = MyReader (\env -> f_a_to_b env (f_a env))
这应该很容易 - pure a 是一个忽略环境并无论如何返回给pure 的值的阅读器。
而你 <*> 两个读者通过为两者提供环境(我希望命名有所帮助 - 第一个参数是 MyReader env (a -> b) 第二个是 MyReader env a,结果应该是 MyReader env b)
现在我们可以定义 monad 实例了:
instance Monad (MyReader env) where
(MyReader f_a) >>= f_a_MyR_b =
MyReader (\env -> let a = f_a env in runReader (f_a_MyR_b a) env)
所以这(作为应用程序)将提供给结果MyReader 的环境传递给两个部分。这里使用runReader 让它更简单一些(不想再次解包)。
现在对于ask,这似乎有点特别:它将传递的环境 正确地进入 monad。您可能可以从它的类型中弄清楚:ask :: MyReader env env try! 但这里是:
ask :: MyReader env env
ask = MyReader (\env -> env)
回到你的问题:
问怎么能返回任何东西?
只有当你runReader它时才会这样做:
举个简单的例子:
tom :: Reader String String
tom = do
env <- ask -- gives you the environment which in this case is a String
return (env ++ " This is Tom.")
它不适合:
tom = ask >>= (\a -> pure (a ++ " This is Tom."))
所以如果你这样做 runReader tom "Who is this" 并遵循上面的所有定义,你最终会得到
runReader tom "Who is this"
= let a = (\env -> env) "Who is this" in (\env -> a ++ " This is Tom.") "Who is this"
= let a = "Who is this" in a ++ " This is Tom."
= "Who is this This is Tom".
不要自己定义Functor/Applicative
{-# LANGUAGE DeriveFunctor #-}
import Control.Monad (ap)
newtype MyReader env a =
MyReader { runReader :: env -> a }
deriving Functor
instance Applicative (MyReader env) where
pure = return
(<*>) = ap
instance Monad (MyReader env) where
return a = MyReader (const a)
(MyReader mr_a) >>= f_a_mr_b =
MyReader (\env -> let a = mr_a env in runReader (f_a_mr_b a) env)
ask :: MyReader env env
ask = MyReader id