我正在包围我的头围绕州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)
在这里,%% =只是告诉我们,一旦我们通过我们的镜头放大到适当的工作人员,我们将执行什么状态操作.