通过 Monocle 修改地图

Modifying Map via Monocle

我想试试镜头,Monocle 库似乎(从我的菜鸟的角度来看)与所有那些花哨的无样板 @Lenses 相得益彰。不幸的是,我发现几乎没有适合初学者的学习材料(我知道 vanilla Scala 中 FP 的基础知识,没有 Scalaz)。官方教程缺少简单的示例(and/or 他们的结果)并且混入了相当复杂的 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)

我看到了 atindex,但没有简单的示例(只是一些奇怪的 [对我来说很神奇] 用 applyOptional 回答需要样板文件)。我想更新地图 - houses in Town。我正在本着这种精神尝试一些事情:

(World.town ^|-> Town.houses ^|-> index((x, y)) ^|-> House.presentsDelivered)
  .modify { _ + 1 }(world)

这在语法上是错误的,但我认为我想做的很明显(在指定的 x, y 坐标处修改 HousepresentsDelivered。所以我的问题是,如何修改 index 部分以访问地图?

欢迎任何帮助、线索或新手友好的学习材料提示。

从字面上看,您离解决方案还差一个字符(也许是一个输入):

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 和其他为案例 class 成员生成的宏观镜头。那些 不能 指向一个值,而 index 如果在地图中给定索引处没有值,则可能会失败。从Monocle的类型来看,index((x, y))Optional[Houses, House],而World.townLens[World, Town]

可选镜头在某种意义上比镜头弱,一旦你用可选镜头组合了一个镜头,即使你组合了更多镜头,你也会继续有可选镜头。所以下面是一个镜头:

World.town ^|-> Town.houses

但这是可选的:

World.town ^|-> Town.houses ^|-? index((0, 0)) ^|-> House.presentsDelivered

Monocle一贯使用x ^|-> y来组合不同类型的x(镜头、可选、遍历等)与镜头,x ^|-? y来组合不同的x带有选项。我个人觉得运算符有点混乱,更喜欢 composeLenscomposeOptional 等,但口味各不相同,如果你想记住运算符,你至少可以确信它们的使用是一致的——您只需要知道给定类型需要哪一个。

您的代码的另一个潜在问题是您不能只这样写:

import monocle.function.all.index

val houses: monocle.Optional[Houses, House] = index((0, 0))

这不会自行编译,因为 index 需要一个 Index 类型的实例 class 作为索引的类型(在本例中 Map[(Int, Int), House]. Monocle 为可以使用的地图提供了一个通用实例,但您必须导入它:

import monocle.std.map._

恐怕我没有什么非常好的学习资料建议,但是你可以随时在这里提问,而且 Monocle Gitter channel 相当活跃。