首页 文章

状态转换与无形状态monad

提问于
浏览
7

Scalaz State monad的modify具有以下签名:

def modify[S](f: S => S): State[S, Unit]

这允许状态被相同类型的状态替换,当状态包括无形值(例如 Record ,其类型随着添加新字段而改变)时,该状态不能很好地工作 . 在这种情况下,我们需要的是:

def modify[S, T](f: S => T): State[T, Unit]

什么是使Scalaz的State monad适应无形状态的好方法,以便人们可以使用记录而不是可怕的 Map[String, Any]

例:

case class S[L <: HList](total: Int, scratch: L)

def contrivedAdd[L <: HList](n: Int): State[S[L], Int] =
  for {
    a <- init
    _ <- modify(s => S(s.total + n, ('latestAddend ->> n) :: s.scratch))
    r <- get
  } yield r.total

Update:

Travis答案的完整代码是here .

1 回答

  • 8

    State 是更通用类型 IndexedStateT 的类型别名,它专门用于表示将状态类型更改为状态计算的函数:

    type StateT[F[_], S, A] = IndexedStateT[F, S, S, A]
    type State[S, A] = StateT[Id, S, A]
    

    虽然无法使用 State 编写 modify[S, T] ,但可以使用 IndexedState (这是 IndexedStateT 的另一种类型别名,将效果类型修复为 Id ):

    import scalaz._, Scalaz._
    
    def transform[S, T](f: S => T): IndexedState[S, T, Unit] =
      IndexedState(s => (f(s), ()))
    

    你甚至可以在 for -comprehensions中使用它(这对我来说似乎有点奇怪,因为monadic类型在操作之间改变,但它有效):

    val s = for {
      a <- init[Int];
      _ <- transform[Int, Double](_.toDouble)
      _ <- transform[Double, String](_.toString)
      r <- get
    } yield r * a
    

    然后:

    scala> s(5)
    res5: scalaz.Id.Id[(String, String)] = (5.0,5.05.05.05.05.0)
    

    在你的情况下,你可能写这样的东西:

    import shapeless._, shapeless.labelled.{ FieldType, field }
    
    case class S[L <: HList](total: Int, scratch: L)
    
    def addField[K <: Symbol, A, L <: HList](k: Witness.Aux[K], a: A)(
      f: Int => Int
    ): IndexedState[S[L], S[FieldType[K, A] :: L], Unit] =
      IndexedState(s => (S(f(s.total), field[K](a) :: s.scratch), ()))
    

    然后:

    def contrivedAdd[L <: HList](n: Int) = for {
      a <- init[S[L]]
      _ <- addField('latestAdded, n)(_ + n)
      r <- get
    } yield r.total
    

    (这可能不是分解更新操作的最佳方法,但它显示了基本思想的工作原理 . )

    它关心将状态转换表示为状态计算,你可以在任何旧的 State 上使用 imap

    init[S[HNil]].imap(s =>
      S(1, field[Witness.`'latestAdded`.T](1) :: s.scratch)
    )
    

    这不允许您以相同的方式在组合上使用这些操作,但在某些情况下可能只需要它们 .

相关问题