微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Scala状态monad – 结合不同的状态类型

我正在包围我的头围绕州monad.简单的例子很容易理解.我现在正在移动到一个现实世界的情况下,域对象是复合的.例如,使用以下域对象(它们没有什么意义,只是纯粹的例子):

case class Master(workers: Map[String,Worker])
case class Worker(elapsed: Long,result: Vector[String])
case class Message(workerId: String,work: String,elapsed: Long)

考虑到工人作为S类型的国家[S,A] monad很容易写出一些组合者,如下所示:

type WorkerState[+A] = State[Worker,A]
def update(message: Message): WorkerState[Unit] = State.modify { w =>
    w.copy(elapsed = w.elapsed + message.elapsed,result = w.result :+ message.work)
}
def getWork: WorkerState[Vector[String]] = State { w => (w.result,w) }
def getElapsed: WorkerState[Long] = State { w => (w.elapsed,w) }
def updateAndGetElapsed(message: Message): WorkerState[Long] = for {
    _ <- update(message)
    elapsed <- getElapsed
} yield elapsed
// etc.

将这些与主状态组合器相结合的惯用方法是什么?例如

type MasterState[+A] = State[Master,A]
def updateAndGetelapsedtime(message: Message): MasterState[Option[Long]]

我可以这样实现:

def updateAndGetelapsedtime(message: Message): MasterState[Option[Long]] =   
    State { m =>
        m.workers.get(message.workerId) match {
            case None => (None,m)
            case Some(w) =>
                val (t,newW) = updateAndGetElapsed(message).run(w)
                (Some(t),m.copy(m.workers.updated(message.workerId,newW))
        }
    }

我不喜欢的是我必须在最后一个变压器内手动运行状态monad.我的现实世界的例子有更多的参与.通过这种方法,它很快就会变得凌乱.

有更多惯用的方式来运行这种增量更新吗?

解决方法

通过组合镜头和状态monad可以很好地做到这一点.首先为安装程序(我已经编辑你的轻轻地让它编译与Scalaz 7.1):

case class Master(workers: Map[String,elapsed: Long)

import scalaz._,Scalaz._

type WorkerState[A] = State[Worker,A]

def update(message: Message): WorkerState[Unit] = State.modify { w =>
  w.copy(
    elapsed = w.elapsed + message.elapsed,result = w.result :+ message.work
  )
}

def getWork: WorkerState[Vector[String]] = State.gets(_.result)
def getElapsed: WorkerState[Long] = State.gets(_.elapsed)
def updateAndGetElapsed(message: Message): WorkerState[Long] = for {
  _ <- update(message)
  elapsed <- getElapsed
} yield elapsed

而现在的几个通用镜头,让我们看看一个硕士:

val workersLens: Lens[Master,Map[String,Worker]] = Lens.lensu(
  (m,ws) => m.copy(workers = ws),_.workers
)

def workerLens(workerId: String): PLens[Master,Worker] =
  workersLens.partial andThen PLens.mapVPLens(workerId)

然后我们基本完成了:

def updateAndGetelapsedtime(message: Message): State[Master,Option[Long]] =
  workerLens(message.workerId) %%= updateAndGetElapsed(message)

在这里,%% =只是告诉我们,一旦我们通过我们的镜头放大到适当的工作人员,我们将执行什么状态操作.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐