我的计划状态包含三个值a
,b
和c
,类型为A
,B
和{{1} }}。不同的功能需要访问不同的值。我想使用C
monad编写函数,以便每个函数只能访问它需要访问的状态部分。
我有以下四种类型的功能:
State
以下是我在f :: State (A, B, C) x
g :: y -> State (A, B) x
h :: y -> State (B, C) x
i :: y -> State (A, C) x
内致电g
的方式:
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
函数是一个丑陋的样板,除了弥合类型g'
和(A, B, C)
之间的差距之外什么都不做。它基本上是(A, B)
的一个版本,它以3元组状态运行,但保留第3个元组项目不变。我正在寻找一种在没有该样板的情况下编写g
的方法。也许是这样的:
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
and f
或g
⊆F
的任何两个函数G
和G
才有效,其中F
是F
和f
所需的状态值集合,G
所需的状态值集合。我错了吗? (请注意,我的示例不满足此属性。例如,g
= G
和{a, b}
= H
。两者都不是另一个的子集。)(我提到的功能看起来像这样。)
{b, c}
答案 0 :(得分:9)
您可以使用lens-family
或lens
包中的tuple-lenses
包进行缩放:zoom
的简化类型为:
zoom :: Lens' s a -> State a x -> State s x
所以zoom
使用较小的状态运行计算。 Lens
用于指定较大状态a
内较小状态s
的位置。
使用这两个软件包,您可以按以下方式运行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)
答案 1 :(得分:4)
如果你不想用元组大惊小怪,你可以使用&#34; classy&#34;有记录的方法。有一些奇特的模板Haskell在lens
包中支持这个,但你也可以手工完成。我们的想法是为州的每一部分创建至少一个类:
class HasPoints s where
points :: Lens' s Int
class ReadsPoints s where
getPoints :: Getter s Int
default getPoints :: HasPoints s => Getter s Int
getPoints = points
class SetsPoints s where
setPoints :: Setter' s Int
...
然后,每个操纵状态的函数都会有一个类型签名,如
fight :: (HasPoints s, ReadsHealth s) => StateT s Game Player
具有此特定签名的操作具有对点的完全访问权限,以及对运行状况的只读访问权限。