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.town
and the other macro-generated lenses for case class members. Those can't not point to a value, whileindex
can 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.town
is 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 ^|-> y
to compose different types ofx
(lenses, optionals, traversals, etc.) with lenses, andx ^|-? y
to compose differentx
s 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
index
requires an instance of theIndex
type 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.