通过 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)
我看到了 at
和 index
,但没有简单的示例(只是一些奇怪的 [对我来说很神奇] 用 applyOptional
回答需要样板文件)。我想更新地图 - houses
in Town
。我正在本着这种精神尝试一些事情:
(World.town ^|-> Town.houses ^|-> index((x, y)) ^|-> House.presentsDelivered)
.modify { _ + 1 }(world)
这在语法上是错误的,但我认为我想做的很明显(在指定的 x, y
坐标处修改 House
的 presentsDelivered
。所以我的问题是,如何修改 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.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
类型的实例 class 作为索引的类型(在本例中 Map[(Int, Int), House]
. Monocle 为可以使用的地图提供了一个通用实例,但您必须导入它:
import monocle.std.map._
恐怕我没有什么非常好的学习资料建议,但是你可以随时在这里提问,而且 Monocle Gitter channel 相当活跃。
我想试试镜头,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)
我看到了 at
和 index
,但没有简单的示例(只是一些奇怪的 [对我来说很神奇] 用 applyOptional
回答需要样板文件)。我想更新地图 - houses
in Town
。我正在本着这种精神尝试一些事情:
(World.town ^|-> Town.houses ^|-> index((x, y)) ^|-> House.presentsDelivered)
.modify { _ + 1 }(world)
这在语法上是错误的,但我认为我想做的很明显(在指定的 x, y
坐标处修改 House
的 presentsDelivered
。所以我的问题是,如何修改 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.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
类型的实例 class 作为索引的类型(在本例中 Map[(Int, Int), House]
. Monocle 为可以使用的地图提供了一个通用实例,但您必须导入它:
import monocle.std.map._
恐怕我没有什么非常好的学习资料建议,但是你可以随时在这里提问,而且 Monocle Gitter channel 相当活跃。