我的程序状态由三个值 a
, b
和 c
组成,类型为 A
, B
和 C
. 不同的功能需要访问不同的值 . 我想使用 State
monad编写函数,以便每个函数只能访问它需要访问的状态段 .
我有以下四种类型的功能:
f :: State (A, B, C) x
g :: y -> State (A, B) x
h :: y -> State (B, C) x
i :: y -> State (A, C) x
以下是我在 f
中调用 g
的方法:
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
那个 g'
函数是一个丑陋的样板,它只能弥补 (A, B, C)
和 (A, B)
类型之间的差距 . 它基本上是 g
的一个版本,它以3元组状态运行,但保留第3个元组项目 . 我正在寻找一种方法来编写没有该样板的 f
. 也许是这样的:
f = do
-- stuff
x <- convert (0,1,2) (g y)
-- more stuff
其中 convert (0,1,2)
将类型 State (a, b) x
的计算转换为类型 State (a, b, c) x
. 同样,对于所有类型 a
, b
, c
, d
:
-
convert (2,0,1)
将State (c,a) x
转换为State (a,b,c) x
-
convert (0,1)
将State b x
转换为State (a,b) x
-
convert (0,2,1,0)
将State (c,b) x
转换为State (a,b,c,d) x
My questions:
-
有没有比将状态值放在元组中更好的解决方案?我想过使用monad变换器堆栈 . 但是,我认为只有对于任何两个函数
f
和g
,F
⊆G
或G
⊆F
,其中F
是f
和G
所需的状态值集合是g
所需的状态值集合 . 我错了吗? (请注意,我的示例不满足此属性 . 例如,G
={a, b}
和H
={b, c}
. 两者都不是另一个的子集 . ) -
如果没有比元组更好的方法,那么有没有一种方法可以避免我提到的样板?我甚至愿意用一堆样板函数编写一个文件(见下文),只要该文件可以自动生成一次然后被遗忘 . 有没有更好的办法? (我读过有关镜头的内容,但它们的复杂性,难看的语法,大量不必要的功能以及对模板Haskell的依赖都令人反感 . 这是对我的误解吗?镜头可以解决我的问题以避免这些问题?)
(我提到的功能看起来像这样 . )
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
2 回答
您可以使用lens-family中的缩放或tuple-lenses包中的lens包来实现:简化类型
zoom
是:所以
zoom
使用较小的状态运行计算 .Lens
用于指定较大状态s
内较小状态a
的位置 .使用这两个包,您可以运行
g
,h
和i
,如下所示:如果你想在
lens
包中使用一些奇特的模板Haskell来支持它,你也可以手工完成 . 这个想法是为州的每个部分创建至少一个类:然后,每个操纵状态的函数都会有类型签名
具有此特定签名的操作具有对点的完全访问权限以及对运行状况的只读访问权限 .