我的程序的状态由三个值组成,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
下面是如何在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)
类型之间的差距之外,什么也不做。它基本上是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
我的问题:
f
和g
),F
⊆G
或G
⊆F
( F
是f
所需的状态值集,G
是g
所需的状态值集)才能工作。我是不是弄错了?(请注意,我的示例不满足此属性。例如,G
= {a, b}
和H
= {b, c}
。两者都不是对方的子集。)(我提到的函数应该是这样的。)
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
发布于 2015-11-26 09:10:03
您可以使用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)
发布于 2015-11-26 11:59:23
如果你不想在元组上大惊小怪,你可以用一种有记录的“优雅”方法。在lens
包中有一些很好的模板Haskell来支持这一点,但是您也可以手工完成。这样做的目的是为状态的每个部分创建至少一个类:
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
具有此特殊签名的操作可以完全访问各个点,并可只读访问健康。
https://stackoverflow.com/questions/33935835
复制