提问人:Mafaldo Reliedo 提问时间:10/4/2023 更新时间:10/4/2023 访问量:125
哈斯克尔的读者莫纳德。读者在哪里作为参数传递?
Reader Monad in Haskell. Where is the reader passed as argument?
问:
在此 monad 阅读器示例中:
import Control.Monad.Reader
tom :: Reader String String
tom = do
env <- ask -- gives you the environment which in this case is a String
return (env ++ " This is Tom.")
jerry :: Reader String String
jerry = do
env <- ask
return (env ++ " This is Jerry.")
tomAndJerry :: Reader String String
tomAndJerry = do
t <- tom
j <- jerry
return (t ++ "\n" ++ j)
runJerryRun :: String
runJerryRun = (runReader tomAndJerry) "Who is this?"
我们可以将 ˋdoˋ 符号读作
tomAndJerry = tom >>= \t -> ...
最终你会得到一个字符串。好吧,所以,基本上ˋ(runReader tomAndJerry)“这是谁?ˋ 是一个读者,所以 ˋaskˋ 给出 ˋ这是谁?但是,ˋtomˋ 没有参数名称。询问如何能够运行?
我可以把 ˋtom :: Reader String Stringˋ 想象成类似 C++ 泛型的东西,其中 ˋReaderˋ 在编译时绑定到某个静态类,当你调用 ask 时,它总是会产生“这是谁?因为据我所知,ˋaskˋ 是接受 ˋReader String Stringˋ 的东西,它没有明确地将其作为参数在任何地方。
我知道它以某种方式获得了上下文,但我对它在语言本身中是如何发生的感到好奇。
我没有尝试任何东西,因为它本身不是问题
答:
因为据我所知,它需要一些东西,但它并没有明确地把它作为论据。
ask
Reader String String
Haskell中关于单子的一个常见误解是,单子是立即运行的动作。它们不是,尤其是对于 a 来说,这有点误导,a 不是带有状态的东西,a 描述了 的变化,然后你可以将这些变化链接在一起,这本质上就是这样的单子所做的。State
State Int
State Int
State
出于同样的原因,不是从环境中读取的东西。它基本上是一个“过程”对象,例如在 C# 中是一个委托,然后您可以使用正确的参数调用它。Reader String String
在这种特定情况下,一个对象只能变成使用正确参数“运行”的东西,在这种情况下是初始环境,runReader :: Reader r a -> r -> a
这样做,从而保证你传递一个 .Reader
r
例如,一个简单的例子是 ,其中定义为:State
State
newtype State s a = State (s -> (s, a))
因此,a 基本上是一个函数,它将状态映射到具有(可能)不同状态和返回值的 2 元组。State
s
现在我们可以将两种状态组合在一起:
combine :: State s a1 -> State s a2 -> State s a2
combine (State f1) (State f2) = State (\s -> let (s', _) = f1 s in f2 s')
因此,我们在这里构造一个新对象,首先通过让第一个函数运行将状态映射到新状态,然后将该新状态传递给第二个函数。State
s'
请注意,我们的 combine 函数(本质上是函数)不会运行两者中的任何一个,它只是构造一个将要运行的函数。>>
现在,如果我们想运行该对象,我们需要一个初始状态,因此将如下所示:State
runState
runState :: State s a -> s -> a
runState (State f) s = let (_, a) = f s in a
因此,我们可以首先组合各种对象,这些对象是函数,它们将状态作为参数并返回(可能)不同的状态。我们将其组合成一个函数,该函数采用初始状态,并在每次将状态传递给下一个子步骤,然后我们可以调用具有初始状态的函数来获取结果。State
所以这里 a 看起来像:get
get :: State s s
get = State (\s -> (s, s))
因此,我们将状态作为新状态和返回值返回,但这只有在我们被赋予一个状态时才有效,而这个对象本身没有 .s
State
State
实际上,类型事物不是类型的值,而是类型函数,它接受环境并产生类型值。看看如果我在你的代码中做这个简单的替换,你有多少问题会消失:Reader r a
a
r -> a
r
a
-- skip all imports, so we can use our own ask implementation
ask :: String -> String
ask s = s
tom :: String -> String
tom = do
env <- ask -- gives you the environment which in this case is a String
return (env ++ " This is Tom.")
jerry :: String -> String
jerry = do
env <- ask
return (env ++ " This is Jerry.")
tomAndJerry :: String -> String
tomAndJerry = do
t <- tom
j <- jerry
return (t ++ "\n" ++ j)
它的环境从哪里来?嗯,这是一个函数,它以环境为参数,就是这样!与 :它将其环境作为参数传递给它。ask
tom
好吧,你可能会说,但看起来不像一个接受参数的函数。毕竟,标志的左边没有争论!tom = do {- ... -}
=
你是对的。对于传统的命令式程序员来说,它看起来不像是一个函数。然而,确实如此。不用担心。。。随着时间的流逝,你会习惯的!举一个非常愚蠢的例子:
f = (+)
虽然这看起来不像一个函数(它在符号的左侧没有参数),但它是一个函数。它与原样的功能完全相同。非常相似的定义 或 ,看起来更像是一个函数,含义几乎完全相同。一个稍微不那么愚蠢的例子:=
(+)
f x y = x + y
f x y = (+) x y
g = if somethingComplicated then (+) else (*)
函数只是可以传递、存储在变量中、作为复杂计算结果返回的对象等。他们并不总是需要命名他们的参数才能成为一个函数!
现在让我们稍微浏览一下您的帖子。
好的,所以,基本上是一个读者,这样给.
(runReader tomAndJerry) "Who is this?"
ask
Who is this?
我不喜欢这种措辞。只是一个读者;更完整的短语是 ,而不是读者。tomAndJerry
(runReader tomAndJerry) "Who is this?"
String
但是,没有参数名称。如何能够运行?
tom
ask
没有任何命名的参数,但仍然有一个参数。
我可以认为像C++泛型这样的东西,在编译时绑定到某个静态类,当你调用它时总是产生它?
tom :: Reader String String
Reader
"Who is this?"
当然不是。 不是一个你可以传递参数的东西;它本身就已经是一个动作了。你不能打电话.你可以把它想象成 .你也可以认为这样的东西可以在实施过程中使用,如果你愿意(但没有义务)。ask
Reader
Reader
ask
tom
tom :: Reader String String
tom :: String -> String
ask
ask :: String -> String
tom
因为据我所知,它需要一些东西,但它并没有明确地把它作为论据。
ask
Reader String String
不。请参阅上一点。 是一个,它不需要一个。ask
Reader String String
评论
Reader String String
String -> String
do