3

我正在尝试实现一个不可变的数据结构来模拟 IT 网络和实例(计算机)。这是一个简化版本:

object Sample {

  case class Instance(id: String, flag: Boolean)
  case class Network(id: String, instances: Set[Instance])
  case class Repo(networks: Map[String, Set[Network]])

  // return new Repo with the instance with id == instanceId updated
  // how to do this using lenses?
  def updateInstanceFlag(networksKey: String, instanceId: String, flag: Boolean): Repo = ???
}

updateInstanceFlag 函数应该创建数据的更新副本,并修改相应的实例(id 为 instanceId)。我尝试使用镜头来实现这一点,但代码太复杂了。具体来说,我很难通过更新数据结构来通过 ID 定位实例或网络。从查询中返回可选值也增加了镜头的复杂性。我使用了自己的镜头实现,但没有真正的偏好(我知道 Shapeless、Monocle、Scalaz 的镜头实现)。

我很欣赏人们关于如何维护“真实”不可变数据的想法和经验。

谢谢。

4

1 回答 1

3

如果我能说服您也将instances字段更改Network为 a Map,那么您可以非常合理地做到这一点:

object Sample {

  case class Instance(id: String, flag: Boolean)
  case class Network(id: String, instances: Map[String, Instance])
  case class Repo(networks: Map[String, Set[Network]])

  // return new Repo with the instance with id == instanceId updated
  // how to do this using lenses?
  def updateInstanceFlag(repo: Repo, networksKey: String,
      instanceId: String, flag: Boolean): Repo = {

    val nets0 = repo.networks
    val net0 = nets0(networksKey)  // TODO fail gracefully
    val inst0 = net0.instances(instanceId) // TODO fail gracefully
    val inst1 = inst0.copy(flag = flag)
    val net1 = net0 + (instanceId -> inst1)
    val nets1 = nets0 + (networksKey -> net1)

    repo.copy(networks = nets1)
  }

}

由于代码如何下降和重新上升的对称性可能已经很明显,因此可能值得在一个可以更新单个网络上的实例标志的方法中考虑一些部分:

def updateInstanceFlag(net: Network, instanceId: String,
    flag: Boolean): Repo = {

  val inst1 = net.instances(instanceId).copy(flag = flag)
  net.copy(instances = net.instances + (instanceId -> inst1))
}

def updateInstanceFlag(repo: Repo, networksKey: String,
    instanceId: String, flag: Boolean): Repo = {

  val net1 = updateInstanceFlag(repo.networks(networksKey), instanceId, flag)
  repo.copy(networks = repo.networks + (networksKey -> net1))
}
于 2014-08-12T08:32:49.147 回答