Я обнимаю голову над государственной монадой. Тривиальные примеры легко понять. Теперь я перехожу к реальному миру, когда объекты домена являются составными. Например, со следующими объектами домена (они не имеют большого смысла, просто пример):
case class Master(workers: Map[String, Worker])
case class Worker(elapsed: Long, result: Vector[String])
case class Message(workerId: String, work: String, elapsed: Long)
Учитывая Worker
как S
типы в State[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.
Каков идиоматический способ объединить их с комбинаторами состояний Master
? например.
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))
}
}
Мне не нравится, что мне нужно вручную запустить государственную монаду внутри последнего трансформатора. Мой пример с реальным миром немного связан. При таком подходе он быстро становится беспорядочным.
Есть ли более идиоматический способ запуска такого рода инкрементных обновлений?