我还没有看到很多scalaz state monad的例子 . 有this example但很难理解,似乎只有一个other question堆栈溢出 .
我已经玩了但我会欢迎其他的 . 此外,如果有人可以举例说明为什么 init
, modify
, put
和 gets
用于那将是很好的 .
编辑:here是一个令人敬畏的2小时状态monad演示文稿 .
我还没有看到很多scalaz state monad的例子 . 有this example但很难理解,似乎只有一个other question堆栈溢出 .
我已经玩了但我会欢迎其他的 . 此外,如果有人可以举例说明为什么 init
, modify
, put
和 gets
用于那将是很好的 .
编辑:here是一个令人敬畏的2小时状态monad演示文稿 .
3 回答
我假设,scalaz 7.0.x和以下导入(查看scalaz 6.x的答案历史记录):
状态类型定义为
State[S, A]
,其中S
是状态的类型,A
是要装饰的值的类型 . 创建状态值的基本语法使用State[S, A]
函数:要在初始值上运行状态计算:
状态可以通过函数调用进行 . 要执行此操作而不是
Function[A, B]
,请定义Function[A, State[S, B]]]
. 使用State
功能......然后
for/yield
语法可用于组合函数:这是另一个例子 . 使用
TwoDice()
状态计算填充列表 .使用序列获得
State[Random, List[(Int,Int)]]
. 我们可以提供类型别名 .或者我们可以使用
sequenceU
来推断类型:使用
State[Map[Int, Int], Int]
计算上面列表中和的频率的另一个示例 .freqSum
计算投掷的总和并计算频率 .现在使用遍历将
freqSum
应用于tenDoubleThrows
.traverse
相当于map(freqSum).sequence
.或者更简洁地使用
traverseU
来推断类型:请注意,因为
State[S, A]
是StateT[Id, S, A]
的类型别名,所以tenDoubleThrows2最终被输入Id
. 我使用copoint
将其变回List
类型 .简而言之,使用状态的关键似乎是让函数返回一个函数来修改状态和所需的实际结果值...免责声明:我从未在 生产环境 代码中使用
state
,只是试图了解它 .Additional info on @ziggystar comment
我放弃尝试使用
stateT
可能是其他人可以显示是否可以扩充StateFreq
或StateRandom
来执行组合计算 . 我找到的是两个状态变换器的组成可以这样组合:它基于
g
是一个参数函数,它取第一个状态变换器的结果并返回一个状态变换器 . 然后以下工作:这是一个关于如何使用
State
的一个非常小的例子:让我们定义一个小型“游戏”,其中一些游戏单位正在与老板(也是游戏单位)作战 .
当游戏开启时我们想要跟踪游戏状态,所以让我们用状态monad来定义我们的“动作”:
让我们努力击中老板让他从他的_504855失去10:
老板可以反击!当他做党内的每个人失去5
health
.现在我们可以将这些动作组成
play
:当然,在现实生活中,这个剧本会更有活力,但对于我的小例子来说,它就足够了:)
我们现在可以运行它来查看游戏的最终状态:
因此,我们几乎没有击中老板,其中一个单位已经死亡,RIP .
这里的重点是构图 .
State
(这只是一个函数S => (A, S)
)允许您定义生成结果的操作,并且在不了解状态来源的情况下操纵某些状态 .Monad
部分为您提供合成,以便您的行动可以组成:等等 .
附:至于
get
,put
和modify
之间的差异:modify
可以一起被视为get
和put
:或简单地说
因此,当您使用
modify
时,您在概念上使用get
和put
,或者您可以单独使用它们 .我偶然发现了一个有趣的博客文章Grok Haskell Monad Transformers来自sigfp,它有一个通过monad变换器应用两个状态monad的例子 . 这是一个scalaz翻译 .
first example 显示
State[Int, _]
monad:所以我在这里有一个使用
init
和modify
的例子 . 在玩了一下之后,init[S]
变得非常方便生成State[S,S]
值,但它允许的另一件事是访问for comprehension中的状态 .modify[S]
是一种方便的方法来转换for comprehension中的状态 . 所以上面的例子可以解读为:a <- init[Int]
:以Int
状态开始,将其设置为State[Int, _]
monad包装的值并将其绑定到a
_ <- modify[Int](_ + 1)
:递增Int
状态b <- init[Int]
:采取Int
状态并将其绑定到b
(与a
相同但现在状态增加)使用
a
和b
产生State[Int, (Int, Int)]
值 .for comprehension语法已经使得在
State[S, A]
中A
侧工作变得微不足道 .init
,modify
,put
和gets
提供一些工具在State[S, A]
中S
方面工作 .博客文章中的 second example 转换为:
与
test1
完全相同的解释 .third example 更棘手,我希望有一些更简单的东西我还没有发现 .
在该代码中,
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
元素中: