You are literally one character (and possibly an import) from the solution:
import monocle.function.all.index import monocle.std.map._ ( World.town ^|-> Town.houses ^|-? index((0, 0)) ^|-> House.presentsDelivered ).modify(_ + 1)
Note that I replaced ^|-> immediately preceding the index with ^|-? . This is necessary because index((x, y)) fundamentally different from World.town and other macro- World.town lenses for members of the case class. They cannot indicate a value, and index can fail if there is no value in a given index on the map. In terms of types, Monocle index((x, y)) is Optional[Houses, House] , and World.town is Lens[World, Town] .
Options are weaker in sense than lenses, and as soon as you compose a lens with an option, you will have additional options, even if you create more lenses. So the lens:
World.town ^|-> Town.houses
But this is optional:
World.town ^|-> Town.houses ^|-? index((0, 0)) ^|-> House.presentsDelivered
The monocle sequentially uses x ^|-> y to create various types of x (lenses, optional, bypasses, etc.) with lenses and x ^|-? y x ^|-? y to create different x with options. I personally think that operators are a little confused and prefer composeLens , composeOptional , etc., but the tastes are different, and if you want to remember the operators, you can at least be sure that they are used sequentially - you just need to know which one you need for this type.
Another potential problem with your code is that you cannot just write this:
import monocle.function.all.index val houses: monocle.Optional[Houses, House] = index((0, 0))
This does not compile on its own because index requires an instance of a class of type index for the type into which it is indexed (in this case Map[(Int, Int), House] ). Monocle provides a generic instance for cards that will work, but you must import it:
import monocle.std.map._
I’m afraid that I don’t have very good suggestions for training materials, but you can always ask questions here, and the Monocle Gitter channel is quite active.