Let's imagine I have a nested immutable object graph, along these lines (using Kotlin syntax, but hopefully it's clear):
data class House(val bedroom: Bedroom, val bathroom: Bathroom, val kitchen: Kitchen)
class Bedroom
class Bathroom
data class Kitchen(val oven: Oven, val kettle: Kettle)
class Oven
data class Kettle(val on: Boolean)
var house = House(Bedroom(), Bathroom(), Kitchen(Oven(), Kettle(false)))
Now, I want to switch the kettle on. If the objects were mutable I would just write:
data class Kettle(var on: Boolean) {
fun activate() {
this.on = true
}
}
house.kitchen.kettle.activate()
But because they are immutable I have to write:
data class Kettle(val on: Boolean) {
fun activate(house: House): House {
return house.copy(kitchen = kitchen.copy(kettle = kettle.copy(on = true)))
}
}
house = house.kitchen.kettle.activate(house)
(Actually, it's slightly more complicated, but this pseudo-code will do).
I don't like this, not because it's long, per se, but because the Kettle now needs to know not just about its own internal state, but about the full context it exists in.
How can I rewrite this so that each object can be responsible for providing its own mutation logic, without having to be aware of the full object graph? Or am I just trying to marry object-oriented and functional concepts in an impossible way?