Scala 具有无形状状态单元组的状态转换

Scala 具有无形状状态单元组的状态转换,scala,scalaz,shapeless,state-monad,scalaz7,Scala,Scalaz,Shapeless,State Monad,Scalaz7,Scalaz State monad的签名如下: def modify[S](f:S=>S):状态[S,单位] 这允许将状态替换为同一类型的状态,当状态包含一个不成形的值(如记录),其类型随着新字段的添加而改变时,该状态将无法正常工作。在这种情况下,我们需要的是: def modify[S,T](f:S=>T):状态[T,单位] 有什么好方法可以使Scalaz的State monad使用不成形状态,这样就可以使用记录,而不是可怕的映射[String,Any] 例如: 案例类S[L状态是更通用

Scalaz State monad的签名如下:

def modify[S](f:S=>S):状态[S,单位]
这允许将状态替换为同一类型的状态,当状态包含一个不成形的值(如
记录
),其类型随着新字段的添加而改变时,该状态将无法正常工作。在这种情况下,我们需要的是:

def modify[S,T](f:S=>T):状态[T,单位]
有什么好方法可以使Scalaz的State monad使用不成形状态,这样就可以使用记录,而不是可怕的
映射[String,Any]

例如:


案例类S[L
状态
是更通用的类型
IndexedStateT的类型别名,专门设计用于表示将状态类型更改为状态计算的函数:

type StateT[F[_], S, A] = IndexedStateT[F, S, S, A]
type State[S, A] = StateT[Id, S, A]
虽然无法使用
状态编写
修改[s,T]
,但可以使用
索引状态
(这是
索引状态
的另一个类型别名,将效果类型固定为
Id
):

您甚至可以在
中使用它来理解
(这对我来说总是有点奇怪,因为一元类型在操作之间会发生变化,但它可以工作):

在您的情况下,您可以这样写:

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 modify[S,t](f:S=>t):State[t,Unit]=State((S:S)=>(f(S),())
?@knutwalker您是否建议扩展
状态
并重载
修改
?不,只需在代码中的任意位置编写此函数。无需扩展或重载任何内容。您的答案除了优雅之外,还包含一块整洁的小宝石,即扩展记录的
添加字段
唱一个键和一个值。见证和
字段[K](a)
的组合在读取不成形源时不明显。
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
init[S[HNil]].imap(s =>
  S(1, field[Witness.`'latestAdded`.T](1) :: s.scratch)
)