title: Updating nested immutable data structures
body: <p>I want to update a nested, immutable data structure (I attached a small example of a hypothetical game.) And I wonder whether this can be done a little more elegantly.</p>
  
  <p>Every time something inside the dungeon changes we need a new dungeon. So, I gave it a general update member. The best way to use this, that I could come up with for the general case, is to specify the processing functions for each nesting and than pass the combined function to the update member.</p>
  
  <p>Then, for really common cases (like applying a map to all the monsters on a specific level), I provide extra members (<code>Dungeon.MapMonstersOnLevel</code>). </p>
  
  <p>The whole thing works, I would just like to know, if anyone can think of better ways of doing it. </p>
  
  <p>Thanks!</p>
  
      
  
  <pre><code>// types
  type Monster(awake : bool) = 
      member this.Awake = awake
  
  type Room(locked : bool, monsters : Monster list) = 
      member this.Locked = locked
      member this.Monsters = monsters
  
  type Level(illumination : int, rooms : Room list) = 
      member this.Illumination = illumination
      member this.Rooms = rooms
  
  type Dungeon(levels : Level list) = 
      member this.Levels = levels
  
      member this.Update levelFunc = 
          new Dungeon(this.Levels |&gt; levelFunc)
  
      member this.MapMonstersOnLevel (f : Monster -&gt; Monster) nLevel =
          let monsterFunc = List.map f
          let roomFunc = List.map (fun (room : Room) -&gt; new Room(room.Locked, room.Monsters |&gt; monsterFunc))
          let levelFunc = List.mapi (fun i (level : Level) -&gt; if i = nLevel then new Level(level.Illumination, level.Rooms |&gt; roomFunc) else level)
          new Dungeon(this.Levels |&gt; levelFunc)
  
      member this.Print() = 
          this.Levels 
          |&gt; List.iteri (fun i e -&gt; 
              printfn "Level %d: Illumination %d" i e.Illumination
              e.Rooms |&gt; List.iteri (fun i e -&gt;
                  let state = if e.Locked then "locked" else "unlocked"
                  printfn "  Room %d is %s" i state
                  e.Monsters |&gt; List.iteri (fun i e -&gt;
                      let state = if e.Awake then "awake" else "asleep"
                      printfn "    Monster %d is %s" i state)))
  
  // generate test dungeon
  let m1 = new Monster(true)
  let m2 = new Monster(false)
  let m3 = new Monster(true)
  let m4 = new Monster(false)
  let m5 = new Monster(true)
  let m6 = new Monster(false)
  let m7 = new Monster(true)
  let m8 = new Monster(false)
  let r1 = new Room(true, [ m1; m2 ])
  let r2 = new Room(false, [ m3; m4 ])
  let r3 = new Room(true, [ m5; m6 ])
  let r4 = new Room(false, [ m7; m8 ])
  let l1 = new Level(100, [ r1; r2 ])
  let l2 = new Level(50, [ r3; r4 ])
  let dungeon = new Dungeon([ l1; l2 ])
  dungeon.Print()
  
  // toggle wake status of all monsters
  let dungeon1 = dungeon.MapMonstersOnLevel (fun m -&gt; new Monster(not m.Awake)) 0
  dungeon1.Print()
  
  // remove monsters that are asleep which are in locked rooms on levels where illumination &lt; 100 and unlock those rooms
  let monsterFunc2 = List.filter (fun (monster : Monster) -&gt; monster.Awake)
  let roomFunc2 = List.map(fun (room : Room) -&gt; if room.Locked then new Room(false, room.Monsters |&gt; monsterFunc2) else room)
  let levelFunc2 = List.map(fun (level : Level) -&gt; if level.Illumination &lt; 100 then new Level(level.Illumination, level.Rooms |&gt; roomFunc2) else level)
  let dungeon2 = dungeon.Update levelFunc2
  dungeon2.Print()
  </code></pre>