简体   繁体   English

为什么 state 和 reader monad 是函数,而 writer monad 是一个元组?

[英]why are the state and reader monads functions while the writer monad is a tuple?

I'm a Haskell newbie, and I think I understand monads and their mechanics (at least for the list, state, maybe, writer and reader monads), but I want to understand why they have been defined the way they have, or why they have to be the way they are, to aid in my intuition in thinking about them.我是 Haskell 新手,我想我了解 monad 及其机制(至少对于列表,状态,也许,作者和读者 monad),但我想了解为什么它们被定义为它们的方式,或者为什么它们必须保持原样,以帮助我凭直觉思考它们。

Specifically, what is it about reading that makes the reader or state monads need to be functions (ie \\s -> (a,s) ), and not just data like the writer monad (ie (w,a) )?具体来说,是什么让阅读器或状态 monad 需要成为函数(即\\s -> (a,s) ),而不仅仅是像 writer monad 这样的数据(即(w,a) )?

Also, could a writer monad be used as a state monad, where the log is used as a string representation of state, as long as the MonadPlus functionality is not used?此外,只要不使用MonadPlus功能, MonadPlus monad 是否可以用作状态 monad,其中日志用作状态的字符串表示? Is the monadic function used with writer monads allowed to look at the current log and modify if at will, or is it only the writer monad's bind function that is allowed to look at the log?与 writer monad 一起使用的 monadic 函数是否允许查看当前日志并随意修改,还是仅允许 writer monad 的 bind 函数查看日志?

Also, why are monads defined in terms of monadic functions that with type a -> mb , instead of with type ma -> mb ?另外,为什么 monad 是根据类型为a -> mb的 monadic 函数定义的,而不是类型为ma -> mb What's so natural about a function going from a base type to a monad wrapped type?函数从基本类型到 monad 包装类型有什么自然的地方?

Thanks for your answers.感谢您的回答。

The state and reader monad both depend on the value of whatever state we're interested in. We need to be able to access it otherwise how could we write something like state 和 reader monad 都取决于我们感兴趣的任何 state 的 value。我们需要能够访问它,否则我们怎么能写出类似的东西

foo = do
 i <- get
 return $ if i == 1 then 2 else 3

So our state monad naturally looks like something that takes in a state, does stuff, and produces a new one.所以我们的状态 monad 自然看起来像是接受一个状态,做一些事情,然后产生一个新的东西。

Likewise with the reader monad, we take in some state, magic, and produce an output (but no new state since it's Reader sa <=> s -> a .与 reader monad 类似,我们接受一些状态、魔法并产生输出(但没有新状态,因为它是Reader sa <=> s -> a

Now this is very different than the writer monad.现在这与 writer monad 非常不同。 In the writer, we don't care about what's been stuck in the state previously, all we want to do is take our current state, and jam some more stuff in there.在 writer 中,我们不关心之前状态中卡住了什么,我们要做的就是获取当前状态,并在其中塞入更多内容。 Because our state is a monoid, we have a guaranteed start state mempty and a way to jam stuff in mappend .因为我们的状态是一个幺半群,所以我们有一个保证的起始状态mempty和一种在mappend堵塞东西的mappend This means that we can "run" each computation in an empty context so to speak and just fuse the outputs together and get identical results.这意味着我们可以在空上下文中“运行”每个计算,只需将输出融合在一起即可获得相同的结果。

Ok, now there's about a few questions so I'll go one at a time好的,现在有几个问题,所以我一次去一个

  1. If the writer monad had access to the previously written results, then it's going to look like s -> (s, a) which is the state monad :) So yes, whatever you could do with the state monad could be done with this augmented writer.如果 writer monad 可以访问先前写入的结果,那么它将看起来像s -> (s, a)这是状态 monad :) 所以是的,无论您可以对状态 monad 做什么,都可以通过这个增强完成作家。

    In fact both the StateT and WriterT monads have MonadPlus instances so I'm not sure what you're getting at here..事实上, StateTWriterT monads 都有MonadPlus实例,所以我不确定你在这里得到了什么..

  2. Since there isn't an arbitrary function ma -> a , we need some way to unwrap a computation to view the results ala >>= , and since return :: a -> ma makes it trivial to go the other way, it's more general to go from plain to monadic value.由于没有任意函数ma -> a ,我们需要某种方法来解开计算以查看结果 ala >>= ,并且由于return :: a -> ma使得走另一条路变得微不足道,它更多一般从普通值到一元值。 Otherwise we'd just have these useless IO String , STM Int and whatnot that we couldn't depend on for the rest of our computation since fmap won't let us hoist in more side effects.否则我们只会有这些无用的IO StringSTM Int等等,我们不能依赖于我们的其余计算,因为fmap不会让我们增加更多的副作用。

They're defined differently because they do different things.他们的定义不同,因为他们做不同的事情。

Take the reader monad.以读者单子为例。 Start by thinking about what it means , not about how it works.首先考虑它的含义,而不是它如何工作的。

A computation in the reader monad is one that depends on an extra piece of information, the reader's "environment". reader monad 中的计算依赖于一条额外的信息,即 reader 的“环境”。 So a Reader Env Int is an Int that depends on the environment (of type Env );所以Reader Env Int是一个依赖于环境的IntEnv类型); if I evaluate it with one environment I'll get one Int value, and if I evaluate it with a different environment I'll get another Int value.如果我用一个环境评估它,我会得到一个Int值,如果我用不同的环境评估它,我会得到另一个Int值。 If I don't have an environment I can't know what value the Reader env Int is.如果我没有环境,我就无法知道Reader env Int是什么值。

Now, what kind of value will give me an Int if I give it an Env ?现在,如果我给它一个Env ,什么样的值会给我一个Int A function of type Env -> Int !类型为Env -> Int函数! So that generalises to e -> a being a monad for each e (with a being the type parameter of the monad; (->) e if you like the prefix notation).因此,这可以概括为e -> a是每个e的 monad( a是 monad 的类型参数; (->) e如果您喜欢前缀表示法)。

Now lets think about the meaning of the writer monad.现在让我们考虑一下 writer monad 的含义。 A computation in the writer monad produces a value, but it also produces an extra value "on the side": the "log" value. writer monad 中的计算会产生一个值,但它也会在“侧面”产生一个额外的值:“log”值。 And when we bind together a series of monadic computations from in the writer monad, the log values will be combined (if we require the log type to be a monoid, then this guarantees log values can be combined with no other knowledge about what they are).当我们将来自 writer monad 的一系列 monadic 计算绑定在一起时,日志值将被组合(如果我们要求日志类型是 monoid,那么这保证了日志值可以在没有其他关于它们的知识的情况下组合) )。 So a Writer Log Int is an Int that also comes with value of type Log .所以Writer Log Int是一个Int ,它也带有Log类型的值。

That sounds a lot like simply a pair: (Log, Int) .这听起来很像简单的一对: (Log, Int) And that generalises to (w, a) being a monad for each w (with a being the type parameter of the monad).这概括为(w, a)是每个w的 monad( a是 monad 的类型参数)。 The monoid constraint on w that guarantees we can combine the log values also means that we have an obvious starting value (the identity element for the monoid: mempty ), so we don't need to provide anything to get a value out of a value in the writer monad. w上的幺半群约束保证我们可以组合对数值也意味着我们有一个明显的起始值(幺半群的标识元素: mempty ),所以我们不需要提供任何东西来从一个值中获取一个值在作家 monad 中。

The reasoning for the state monad to be s -> (a, s) is actually pretty much a combination of the above;状态 monad 为s -> (a, s)原因实际上几乎是上述的组合; a State S Int is an Int that both depends on an S value (as the reader depends on the environment) and also produces an S value, where binding together a sequence of state computations should result in each one "seeing" the state produced by the previous one. State S Int是一个Int ,它既依赖于S值(因为读取器依赖于环境)也产生一个S值,其中将一系列状态计算绑定在一起应该导致每个人“看到”产生的状态上一个。 A value that depends on a state value is a function of the state value;依赖于状态值的值是状态值的函数; if the output comes "along with" a new state value then we need a pair.如果输出“伴随”一个新的状态值,那么我们需要一对。

Also, why are monads defined in terms of monadic functions that with type a -> mb , instead of with type ma -> mb ?另外,为什么 monad 是根据类型为a -> mb的 monadic 函数定义的,而不是类型为ma -> mb What's so natural about a function going from a base type to a monad wrapped type?函数从基本类型到 monad 包装类型有什么自然的地方?

(I took the libery of adding a space between m and b in mb ). (我在mb自由地在mb之间添加了一个空格)。

See, that's what makes it a monad.看,这就是使它成为单子的原因。 Without this, we already have functions a -> b and a link from these to fa -> fb (this link is called "functor", and obeys the laws for fmap ).没有这个,我们已经有函数a -> b和从这些到fa -> fb链接(这个链接被称为“函子”,并遵守fmap的定律)。 But functor only gives you a projection of one "world" (a category) into the other - so that whatever laws hold in the first world, they also hold in the second world (for example, if a + b == c , then fa (f +) fb == fc ).但是函子只给你一个“世界”(一个类别)到另一个世界的投影——所以无论在第一个世界中适用的定律,它们在第二个世界中也适用(例如,如果a + b == c ,那么fa (f +) fb == fc )。 The monad gives you a bridge between the "worlds". monad 为您提供了“世界”之间的桥梁。

Also, you don't have to define the monad in terms of behaviour with functions of type a -> mb , but one minimal specification of a monad tells you how >>= , return , id and (.) relate.此外,您不必根据a -> mb类型a -> mb函数的行为来定义 monad,但是 monad 的一个最小规范会告诉您>>=returnid(.)关联的。 It is possible to define the monad using >=> , return , id and (.) , or using join , return , id and (.) - you see, it doesn't really matter which function to pick.可以使用>=>returnid(.)或使用joinreturnid(.)定义 monad - 你看,选择哪个函数并不重要。 It turns out, >>= is convenient for chaining.事实证明, >>=便于链接。

Specifically, what is it about reading that makes the reader or state monads need to be functions (ie \\s -> (a,s) ), and not just data like the writer monad (ie (w,a) )?具体来说,是什么让阅读器或状态 monad 需要成为函数(即\\s -> (a,s) ),而不仅仅是像 writer monad 这样的数据(即(w,a) )?

Functions are just data.函数只是数据。 These just happen to be the types which fit the desired semantics best.这些恰好是最适合所需语义的类型。

Also, could a writer monad be used as a state monad, where the log is used as a string representation of state, as long as the MonadPlus functionality is not used?此外,只要不使用 MonadPlus 功能,编写器 monad 是否可以用作状态 monad,其中日志用作状态的字符串表示?

You couldn't pass non-empty initial state, and couldn't modify it other than by appending values.您不能传递非空的初始状态,并且只能通过附加值来修改它。 If you do change this, you get the standard state monad (except for strings only).如果你确实改变了这个,你会得到标准的状态 monad(除了字符串)。

Is the monadic function used with writer monads allowed to look at the current log and modify if at will, or is it only the writer monad's bind function that is allowed to look at the log?与 writer monad 一起使用的 monadic 函数是否允许查看当前日志并随意修改,还是仅允许 writer monad 的 bind 函数查看日志?

You'll note the Writer monad has a tell function which actually adds new data to the log.您会注意到 Writer monad 有一个tell函数,它实际上将新数据添加到日志中。

Also, why are monads defined in terms of monadic functions that with type a -> mb , instead of with type ma -> mb ?另外,为什么 monad 是根据类型为a -> mb的 monadic 函数定义的,而不是类型为ma -> mb What's so natural about a function going from a base type to a monad wrapped type?函数从基本类型到 monad 包装类型有什么自然的地方?

I think the best answer is "because a -> mb turns out to be more useful".我认为最好的答案是“因为a -> mb结果更有用”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM