Scalaz 状态 monad 示例
我还没有看到很多 scalaz 状态单子的例子。有这个示例,但很难理解,并且只有一个其他问题看起来堆栈溢出。
我将发布一些我玩过的例子,但我欢迎更多的例子。另外,如果有人可以提供为什么使用 init
、modify
、put
和 gets
的示例,那就太好了。
编辑:这里是关于状态 monad 的精彩 2 小时演示。
I haven't seen many examples of the scalaz state monad. There is this example but it is hard to understand and there is only one other question on stack overflow it seems.
I'm going to post a few examples I've played with but I would welcome additional ones. Also if somebody can provide example on why init
, modify
, put
and gets
are used for that would be great.
Edit: here is an awesome 2 hours presentation on the state monad.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我假设,scalaz 7.0.x 和以下导入(查看 scalaz 6.x 的答案历史记录):
状态类型定义为
State[S, A]
,其中S
是状态类型,A
是被修饰值的类型。创建状态值的基本语法使用State[S, A]
函数:要对初始值运行状态计算:
可以通过函数调用对状态进行线程化。要执行此操作,请定义
Function[A, State[S, B]]]
,而不是Function[A, B]
。使用State
函数...然后可以使用
for/yield
语法来组成函数:这是另一个例子。用
TwoDice()
状态计算填充列表。使用序列获取
State[Random, List[(Int,Int)]]
。我们可以提供类型别名。或者我们可以使用
sequenceU
来推断类型:另一个使用
State[Map[Int, Int], Int]
的示例来计算上面列表中总和的频率。freqSum
计算抛出的总和并计算频率。现在使用 traverse 在
tenDoubleThrows
上应用freqSum
。traverse
相当于map(freqSum).sequence
。或者更简洁地使用
traverseU
来推断类型:请注意,因为
State[S, A]
是StateT[Id, S, A] 的类型别名
,tenDoubleThrows2 最终被输入为Id
。我使用copoint
将其转回List
类型。简而言之,使用状态的关键似乎是让函数返回修改状态的函数和所需的实际结果值...免责声明:我从未在生产代码中使用过状态,只是想感受一下。
@ziggystar 评论的其他信息
我放弃了尝试使用
stateT
可能其他人可以显示如果StateFreq 或可以增强 StateRandom 来执行组合计算。相反,我发现两个状态变换器的组合可以这样组合:
它的前提是
g
是一个单参数函数,获取第一个状态变换器的结果并返回一个状态变换器。然后以下内容将起作用:I assume, scalaz 7.0.x and the following imports (look at answer history for scalaz 6.x):
The state type is defined as
State[S, A]
whereS
is type of the state andA
is the type of the value being decorated. The basic syntax to create a state value makes use of theState[S, A]
function:To run the state computation on a initial value:
The state can be threaded through function calls. To do this instead of
Function[A, B]
, defineFunction[A, State[S, B]]]
. Use theState
function...Then the
for/yield
syntax can be used to compose functions:Here is another example. Fill a list with
TwoDice()
state computations.Use sequence to get a
State[Random, List[(Int,Int)]]
. We can provide a type alias.Or we can use
sequenceU
which will infer the types:Another example with
State[Map[Int, Int], Int]
to compute frequency of sums on the list above.freqSum
computes the sum of the throws and counts frequencies.Now use traverse to apply
freqSum
overtenDoubleThrows
.traverse
is equivalent tomap(freqSum).sequence
.Or more succinctly by using
traverseU
to infer the types:Note that because
State[S, A]
is a type alias forStateT[Id, S, A]
, tenDoubleThrows2 ends up being typed asId
. I usecopoint
to turn it back into aList
type.In short, it seems the key to use state is to have functions returning a function modifying the state and the actual result value desired... Disclaimer: I have never used
state
in production code, just trying to get a feel for it.Additional info on @ziggystar comment
I gave up on trying using
stateT
may be someone else can show ifStateFreq
orStateRandom
can be augmented to perform the combined computation. What I found instead is that the composition of the two state transformers can be combined like this:It's predicated on
g
being a one parameter function taking the result of the first state transformer and returning a state transformer. Then the following would work:我偶然发现了一篇有趣的博客文章 Grok Haskell Monad Transformers 来自 sigfp,其中有一个通过 monad 转换器应用两个状态 monad 的示例。这是 scalaz 翻译。
第一个示例显示了一个
State[Int, _]
monad:所以我这里有一个使用
init
和modify
的示例代码>.玩了一下之后,init[S]
事实证明生成State[S,S]
值确实很方便,但它允许的另一件事是访问内部的状态以进行理解。modify[S]
是一种转换 for 推导式内部状态的便捷方法。所以上面的例子可以理解为:a <- init[Int]
:以Int
状态开始,将其设置为State包裹的值[Int, _]
monad 并将其绑定到a
_ <- 修改[Int](_ + 1)
:增加Int< /代码>状态
Int
状态并将其绑定到b
(与a
相同,但现在state 递增)a
和b
生成一个State[Int, (Int, Int)]
值。for 理解语法已经使得在
State[S, A]
中的A
端工作变得微不足道。init
、modify
、put
和gets
提供了一些在S
上工作的工具位于状态[S,A]
。博客文章中的第二个示例翻译为:
与
test1
的解释非常相似。第三个例子更加棘手,我希望有一些我尚未发现的更简单的东西。
在该代码中,
stTrans
负责两种状态的转换(增量和后缀为"1"
)以及拉出String
状态。stateT
允许我们在任意 monadM
上添加状态转换。在本例中,状态是一个递增的Int
。如果我们调用 stTrans ! 0 我们最终会得到M[String]
。在我们的示例中,M
是StateString
,因此我们最终会得到StateString[String]
,即State[String, String ]
。这里棘手的部分是我们想要从
stTrans
中提取Int
状态值。这就是initT
的用途。它只是创建一个对象,以我们可以使用stTrans
进行 flatMap 的方式来访问状态。编辑:如果我们真正重用
test1
和test2
,那么所有这些尴尬都是可以避免的,它们可以方便地将想要的状态存储在_2
元素中他们返回的元组:I stumbled on an interesting blog post Grok Haskell Monad Transformers from sigfp that has an example of applying two state monads through a monad transformer. Here is a scalaz translation.
The first example shows a
State[Int, _]
monad:So I have here an example of using
init
andmodify
. After playing with it a bit,init[S]
turns out to be really convenient to generate aState[S,S]
value, but the other thing it allows is to access the state inside the for comprehension.modify[S]
is a convenient way to transform the state inside the for comprehension. So the example above can be read as:a <- init[Int]
: start with anInt
state, set it as the value wrapped by theState[Int, _]
monad and bind it toa
_ <- modify[Int](_ + 1)
: increment theInt
stateb <- init[Int]
: take theInt
state and bind it tob
(same as fora
but now the state is incremented)State[Int, (Int, Int)]
value usinga
andb
.The for comprehension syntax already makes it trivial to work on the
A
side inState[S, A]
.init
,modify
,put
andgets
provide some tools to work on theS
side inState[S, A]
.The second example in the blog post translates to:
Very much the same explanation as
test1
.The third example is more tricky and I hope there is something simpler that I have yet to discover.
In that code,
stTrans
takes care of the transformation of both states (increment and suffix with"1"
) as well as pulling out theString
state.stateT
allows us to add state transformation on an arbitrary monadM
. In this case the state is anInt
that is incremented. If we calledstTrans ! 0
we would end up withM[String]
. In our example,M
isStateString
, so we'll end up withStateString[String]
which isState[String, String]
.The tricky part here is that we want to pull out the
Int
state value out fromstTrans
. This is whatinitT
is for. It just creates an object that gives access to the state in a way we can flatMap withstTrans
.Edit: Turns out all of that awkwardness can be avoided if we truly reused
test1
andtest2
which conveniently store the wanted states in the_2
element of their returned tuples:下面是一个关于如何使用
State
的小示例:让我们定义一个小型“游戏”,其中一些游戏单元正在与 Boss(也是游戏单元)战斗。
当比赛开始时,我们想要跟踪游戏状态,所以让我们用状态单子来定义我们的“动作”:
让我们重击 Boss,让他失去 10 点
生命值
:并且老板可以反击!当他这样做时,队伍中的每个人都会失去 5 点生命值。
现在我们可以将这些动作组合成
游戏
:当然,在现实生活中,游戏会更加动态,但这对于我的小例子来说已经足够了:)
我们可以运行现在来看看游戏的最终状态:
所以我们勉强击中了 Boss,其中一个单位已经死亡,RIP。
这里的重点是构图。
State
(这只是一个函数S => (A, S)
)允许您定义产生结果的操作,并在不知道太多情况下操作某些状态状态是从哪里来的。Monad
部分为您提供组合,以便可以组合您的操作:等等。
PS 至于
get
、put
和modify
之间的区别:可以看出
modify
作为get
和put
在一起:或者简单地
因此,当您使用
modify
时,您在概念上使用get
和put
,或者您可以单独使用它们。Here is a very small example on how
State
can be used:Let's define a small "game" where some game units are fighting the boss (who is also a game unit).
When the play is on we want to keep track of the game state, so let's define our "actions" in terms of a state monad:
Let's hit the boss hard so he loses 10 from his
health
:And the boss can strike back! When he does everyone in a party loses 5
health
.Now we can compose these actions into
play
:Of course in the real life the play will be more dynamic, but it is food enough for my small example :)
We can run it now to see the final state of the game:
So we barely hit the boss and one of the units have died, RIP.
The point here is the composition.
State
(which is just a functionS => (A, S)
) allows you to define actions that produce results and as well manipulate some state without knowing too much where the state is coming from.The
Monad
part gives you composition so your actions can be composed:and so on.
P.S. As for differences between
get
,put
andmodify
:modify
can be seen asget
andput
together:or simply
So when you use
modify
you conceptually useget
andput
, or you can just use them alone.