Immutable data structure in Scala

I am trying to implement an immutable data structure that models IT networks and instances (computers). Here's a simplified version:

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 = ???
}

      

The updateInstanceFlag function should create an updated copy of the data with the corresponding instance (with id instanceId) changed. I tried to implement this with lenses, but the code was too complicated. In particular, I found it difficult to compose an instance or network location by ID with an update to the data structure. Returning Optional values ​​from queries also contributed to lens complexity. I have used my own lens implementation, but have no real preference (I know about lens implementations from Shapeless, Monocle, Scalaz).

I would be grateful to people for thoughts and experience on how to maintain "real" immutable data.

Thank.

+3


source to share


1 answer


If I can convince you to change the field instances

Network

to a Map

, you can do it quite intelligently:

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)
  }

}

      



Since the symmetry in how the descent and re-iteration of the code is likely to become apparent, it might be worthwhile to ditch the partial part of the method that can update the instance flag on the same network:

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))
}

      

+3


source







All Articles