I wanted to try lenses and the Monocle library seemed (from my noobish perspective) good with all those fancy boilerplate-less @Lenses. Unfortunately I found out there are little to non learning materials for beginners (I know basics of FP in vanilla Scala, no Scalaz). Official tutorial lacks easy examples (and/or their results) and mixes in quite complex Scalaz library. One would assume that such trivial task like accessing a Map would be covered on a first page.
I have following snippet:
@Lenses case class House(presentsDelivered: Int)
type Houses = Map[(Int, Int), House]
@Lenses case class Town(houses: Houses)
@Lenses case class Santa(x: Int, y: Int)
@Lenses case class World(santa: Santa, town: Town)
I saw at and index, but no simple examples (just some weird [magic for me] answer with applyOptional which required boilerplate). I want to update the map - houses in Town. I was trying something in this spirit:
(World.town ^|-> Town.houses ^|-> index((x, y)) ^|-> House.presentsDelivered)
.modify { _ + 1 }(world)
Which is syntactically wrong, but I think it's apparent what I wanted to do (modify presentsDelivered of House at specified x, y coordinates). So my question is, how to modify the index part to access the map?
Any help, clue or noob-friendly learning materials tips are welcome.
You're literally one character (and maybe an import) away from the solution:
Note that I've replaced the
^|->immediately preceding the index with^|-?. This is necessary becauseindex((x, y))is fundamentally different fromWorld.townand the other macro-generated lenses for case class members. Those can't not point to a value, whileindexcan fail if there's no value at the given index in the map. In terms of Monocle's types,index((x, y))is anOptional[Houses, House], whileWorld.townis aLens[World, Town].Optionals are weaker in a sense than lenses, and once you've composed a lens with an optional, you're going to continue to have optionals even if you compose more lenses. So the following is a lens:
But this is an optional:
Monocle consistently uses
x ^|-> yto compose different types ofx(lenses, optionals, traversals, etc.) with lenses, andx ^|-? yto compose differentxs with optionals. I personally find the operators a little confusing and prefercomposeLens,composeOptional, etc., but tastes vary, and if you want to memorize the operators you can at least be confident that they're used consistently—you just need to know which one you need for a given type.The other potential issue with your code is that you can't just write this:
This won't compile on its own because
indexrequires an instance of theIndextype class for the type that it's indexing into (in this caseMap[(Int, Int), House]. Monocle provides a generic instance for maps that will work, but you have to import it:I'm afraid I don't have any terribly good suggestions for learning materials, but you can always ask questions here, and the Monocle Gitter channel is fairly active.