Reputation: 31
I am trying to implement an immutable data structure that models IT networks and instances (computers). Here is 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) modified. I tried to implement this using lenses but the code was too complex. Specifically, I had hard time composing the locating of an instance or network by ID with the updating of the data structure. Returning Optional values from queries also contributed to the complexity of the lenses. I used my own implementation of lenses but have no real preference (I know of lenses implementation from Shapeless, Monocle, Scalaz).
I'd appreciate people's thoughts and experiences on how to maintain 'real' immutable data.
Thanks.
Upvotes: 3
Views: 559
Reputation: 14842
If I can convince you to change the instances
field of Network
into a Map
as well, you can do it quite reasonably:
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 code descends and reascends has probably become apparent, it might be worth factoring some part out in a method that can update an instance flag on a single 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))
}
Upvotes: 3