混合并匹配State monad中的有状态计算

时间:2015-11-26 10:11:52

标签: haskell functional-programming monads

我的计划状态包含三个值abc,类型为AB和{{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) xabc

  • dconvert (2,0,1)转换为State (c,a) x
  • State (a,b,c) xconvert (0,1)转换为State b x
  • State (a,b) xconvert (0,2,1,0)转换为State (c,b) x

我的问题:

  1. 有没有比将状态值放在元组中更好的解决方案?我想过使用monad变压器堆栈。但是,我认为只有State (a,b,c,d) x and fgF的任何两个函数GG才有效,其中FFf所需的状态值集合,G所需的状态值集合。我错了吗? (请注意,我的示例不满足此属性。例如,g = G{a, b} = H。两者都不是另一个的子集。)
  2. 如果没有比元组更好的方法,那么有没有一种方法可以避免我提到的样板?我甚至愿意用一堆样板函数编写一个文件(见下文),只要该文件可以自动生成一次然后被遗忘。有没有更好的办法? (我读过关于镜头的内容,但是它们的复杂性,难看的语法,大量不必要的功能,以及对模板Haskell的依赖都令人反感。这是对我的误解吗?镜头可以解决我的问题以避免这些问题?)
  3. (我提到的功能看起来像这样。)

    {b, c}

2 个答案:

答案 0 :(得分:9)

您可以使用lens-familylens包中的tuple-lenses包进行缩放:zoom的简化类型为:

zoom :: Lens' s a -> State a x -> State s x

所以zoom使用较小的状态运行计算。 Lens用于指定较大状态a内较小状态s的位置。

使用这两个软件包,您可以按以下方式运行ghi

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

具有此特定签名的操作具有对点的完全访问权限,以及对运行状况的只读访问权限。