5

我想尝试镜头,Monocle 库似乎(从我的菜鸟的角度来看)与所有那些花哨的无样板文件都很好@Lenses。不幸的是,我发现对于初学者来说几乎没有学习材料(我知道香草 Scala 中的 FP 基础知识,没有 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)

我看到了atand index,但没有简单的例子(只是一些奇怪的 [我的魔法] 答案applyOptional需要样板文件)。我想更新地图housesTown我本着这种精神尝试了一些事情:

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

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

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

4

1 回答 1

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))is an Optional[Houses, House], while World.townis a Lens[World, Town]

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

World.town ^|-> Town.houses

但这是可选的:

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

Monocle 一直使用镜头x ^|-> y来组合不同类型的x(镜头、可选、遍历等),并用可选x ^|-? y来组合不同x的 s。我个人觉得运算符有点混乱,更喜欢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 频道相当活跃。

于 2015-12-09T18:10:16.133 回答