混合并匹配State monad中的有状态计算 [英] Mix and match stateful computations within the State monad
问题描述
我的程序状态由三个值组成: a
, b
和类型
A
, B
和 C
State
monad编写函数,以便每个函数只能访问它需要访问的状态。
我有以下四种类型的函数:
f :: State(A,B,C)x
g :: y - >状态(A,B)x
h :: y - >州(B,C)x
i :: y - >状态(A,C)x
以下是我如何调用 g
f
:
f = do
- 一些东西
- y被绑定到某处某处的表达式
- 更多东西
x< - g'y
- 更多东西
其中g'y = do
(a,b,c)< - get
let(x,(a',b'))= runState(gy)(a ,b)
put(a',b',c)
return x
< p <> code> g'函数是一个丑陋的样板,除了缩小类型(A,B,C)之间的差距外, code>和
(A,B)
。它基本上是一个 g
的版本,它以三元组状态运行,但保持第三个元组项不变。我正在寻找一种方法来编写 f
没有这样的样板。可能是这样的:
pre $
$ stuff
x< - convert(0, 1,2)(gy)
- more stuff
其中 convert(0,1,2)
将类型 State(a,b)x
的计算转换为类型 State (a,b,c)x
。同样,对于所有类型 a
, b
, c
, d
:
-
convert(2,0,1 )将
状态(c,a)x
转换为状态(a,b,c)x
-
convert(0,1)
转换状态bx
code> State(a,b)x -
convert(0,2,1,0)
转换状态(c,b)x
为状态(a,b,c,d)x
我的问题:
$ b
- 是否有比将状态值放入元组更好的解决方案?我想过使用monad变压器堆栈。然而,我认为只有在任何两个函数
f
和g
,F
⊆G
或G
⊆F $ c其中
F
是f
所需的状态值集合,G
是g
所需的一组状态值。我错了吗? (请注意,我的示例不满足此属性,例如,G
={a,b}
和H
={b,c}
两者都不是其他的子集。) - 如果没有比元组更好的方法,那么是否有避免我提到的样板的好方法?我甚至愿意编写一个带有一系列样板函数的文件(见下文),只要该文件可以自动生成一次然后被遗忘。有没有更好的办法? (我已经读过关于镜头的内容,但是他们的复杂性,丑陋的语法,巨大的不必要的功能以及对Haskell模板的依赖都是令人厌恶的。这是我的一个误解吗?镜头能够以避免这些问题的方式解决我的问题吗? ?)
(我提到的函数看起来像这样)。
convert_0_1_2 :: State(a,b)x - >状态(a,b,c)x
convert_0_1_2 f = do
(a,b,c)< - get
let(x,(a',b'))= runState f(a,b)
put(a',b',c)
return x
convert_0_2_1_0 :: State(c,b)x - >状态(a,b,c,d)x
convert_0_2_1_0 f = do
(a,b,c,d)< - get
let(x,(b',c' ))= runState f(b,c)
put(a,b',c',d)
return x
您可以使用 lens-family
或 lens
包与 tuple-lenses
包: zoom
的简化类型是:
zoom :: Lens'sa - >陈述一个x - > State sx
因此 zoom
一个较小的状态。 Lens 用于指定较大状态
s
a
的位置。 / code>。
使用这两个包,您可以运行 g
, h
和 i
如下:
f :: State(A,B,C)x
f = do
zoom _12 g - _12 :: Lens'(A,B,C)(A,B)
zoom _23 h - _23 :: Lens'(A,B,C)(B,C)
zoom _13 i - _13 :: Lens'(A,B,C)(A,C)
The state of my program consists of three values, a
, b
, and c
, of types A
, B
, and C
. Different functions need access to different values. I want to write functions using the State
monad so that each function can only access the pieces of the state that it needs to access.
I have four functions of the following types:
f :: State (A, B, C) x
g :: y -> State (A, B) x
h :: y -> State (B, C) x
i :: y -> State (A, C) x
Here is how I call g
within f
:
f = do
-- some stuff
-- y is bound to an expression somewhere in here
-- more stuff
x <- g' y
-- even more stuff
where g' y = do
(a, b, c) <- get
let (x, (a', b')) = runState (g y) (a, b)
put (a', b', c)
return x
That g'
function is an ugly piece of boilerplate that does nothing but bridge the gap between the types (A, B, C)
and (A, B)
. It is basically a version of g
that operates on a 3-tuple state, but leaves the 3rd tuple item unchanged. I am looking for a way to write f
without that boilerplate. Maybe something like this:
f = do
-- stuff
x <- convert (0,1,2) (g y)
-- more stuff
Where convert (0,1,2)
converts a computation of type State (a, b) x
to type State (a, b, c) x
. Likewise, for all types a
, b
, c
, d
:
convert (2,0,1)
convertsState (c,a) x
toState (a,b,c) x
convert (0,1)
convertsState b x
toState (a,b) x
convert (0,2,1,0)
convertsState (c,b) x
toState (a,b,c,d) x
My questions:
- Is there a better solution than putting state values in tuples? I thought about using a monad transformer stack. However, I think that only works if, for any two functions
f
andg
, eitherF
⊆G
orG
⊆F
, whereF
is the set of state values needed byf
andG
is the set of state values needed byg
. Am I wrong about that? (Note that my example does not satisfy this property. For example,G
={a, b}
andH
={b, c}
. Neither is a subset of the other.) - If there is no better way than tuples, then is there a good way to avoid the boilerplate I mentioned? I am even willing to write a file with a bunch of boilerplate functions (see below) as long as the file can be automatically generated once and then forgotten about. Is there a better way? (I have read about lenses, but their complexity, ugly syntax, enormous set of unnecessary features, and seeming reliance on Template Haskell are off-putting. Is that a misconception of mine? Can lenses solve my problem in a way that avoids those problems?)
(The functions I mentioned would look something like this.)
convert_0_1_2 :: State (a, b) x -> State (a, b, c) x
convert_0_1_2 f = do
(a, b, c) <- get
let (x, (a', b')) = runState f (a, b)
put (a', b', c)
return x
convert_0_2_1_0 :: State (c, b) x -> State (a, b, c, d) x
convert_0_2_1_0 f = do
(a, b, c, d) <- get
let (x, (b', c')) = runState f (b, c)
put (a, b', c', d)
return x
You can do it by using zoom from lens-family
or the lens
package with the tuple-lenses
package: the simplified type of zoom
is:
zoom :: Lens' s a -> State a x -> State s x
So zoom
runs a computation using a smaller state. The Lens
is used to specify the location of smaller state a
inside the larger state s
.
With these two packages, you can run g
, h
and i
as follows:
f :: State (A,B,C) x
f = do
zoom _12 g -- _12 :: Lens' (A,B,C) (A,B)
zoom _23 h -- _23 :: Lens' (A,B,C) (B,C)
zoom _13 i -- _13 :: Lens' (A,B,C) (A,C)
这篇关于混合并匹配State monad中的有状态计算的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!