我想尝试镜头,Monocle图书馆似乎(从我的犹豫不决的角度来看)对所有那些花哨的样板很少@Lenses
。不幸的是我发现初学者几乎没有非学习材料(我知道香草Scala的基础知识,没有Scalaz)。官方教程缺少简单的示例(和/或其结果),并且在非常复杂的Scalaz库中混合使用。人们会认为,访问地图这样的微不足道的任务将覆盖在第一页上。
我有以下代码段:
@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)
我看到at
和index
,但没有简单的例子(只有一些奇怪的[魔法对我来说]回答applyOptional
需要样板文件)。我想在houses
更新地图 - Town
。我正在尝试这种精神:
(World.town ^|-> Town.houses ^|-> index((x, y)) ^|-> House.presentsDelivered)
.modify { _ + 1 }(world)
这在语法上是错误的,但我认为我想做的很明显(在指定的presentsDelivered
坐标处修改House
x, y
)。我的问题是,如何修改index
部分以访问地图?
欢迎任何帮助,线索或noob友好的学习材料提示。
答案 0 :(得分:9)
你实际上只有一个角色(也许是一个导入)远离解决方案:
import monocle.function.all.index
import monocle.std.map._
(
World.town ^|->
Town.houses ^|-?
index((0, 0)) ^|->
House.presentsDelivered
).modify(_ + 1)
请注意,我已使用^|->
替换了索引前面的^|-?
。这是必要的,因为index((x, y))
与World.town
以及案例类成员的其他宏生成镜头根本不同。那些不能指向某个值,而如果地图中给定索引处没有值,index
可能会失败。就Monocle类型而言,index((x, y))
是Optional[Houses, House]
,而World.town
是Lens[World, Town]
。
选项在某种意义上比镜头更弱,一旦你用可选镜头组成镜头,即使你制作了更多镜头,你也会继续选择。所以以下是一个镜头:
World.town ^|-> Town.houses
但这是可选的:
World.town ^|-> Town.houses ^|-? index((0, 0)) ^|-> House.presentsDelivered
Monocle始终使用x ^|-> y
使用镜头构建不同类型的x
(镜头,可选项,遍历等),并使用x ^|-? y
组合不同的x
选配。我个人觉得操作员有点困惑,喜欢composeLens
,composeOptional
等,但口味各不相同,如果你想记住操作员,你至少可以确信他们已经被使用了始终如一 - 您只需要知道给定类型需要哪一个。
您的代码的另一个潜在问题是您不能写这个:
import monocle.function.all.index
val houses: monocle.Optional[Houses, House] = index((0, 0))
这不会自行编译,因为index
需要Index
类型的实例作为其索引类型(在本例中为Map[(Int, Int), House]
Monocle为可以使用的地图提供了一个通用实例,但你必须导入它:
import monocle.std.map._
我担心我没有对学习材料提出任何非常好的建议,但你总是可以在这里提出问题,Monocle Gitter channel相当活跃。