Scala 使用Monocle更新嵌套贴图的正确语法

Scala 使用Monocle更新嵌套贴图的正确语法,scala,monocle-scala,Scala,Monocle Scala,我已经看到了更新地图的方法,但是语法有问题 val pod: Lens[Event, Pod] = GenLens[Event](_.`object`) val metadata: Lens[Pod, Metadata] = GenLens[Pod](_.metadata) val labels: Lens[Metadata, Map[String, String]] = GenLens[Metadata](_.labels) 我想更新标签地图中的按键“app”。但我无法编译以下内容: (la

我已经看到了更新
地图的方法,但是语法有问题

val pod: Lens[Event, Pod] = GenLens[Event](_.`object`)
val metadata: Lens[Pod, Metadata] = GenLens[Pod](_.metadata)
val labels: Lens[Metadata, Map[String, String]] = GenLens[Metadata](_.labels)
我想更新
标签
地图
中的按键“app”。但我无法编译以下内容:

(labels.composeOptional(index("app"))).set("whatever")(someLabels)

事实上,Monacle的一位作者的这篇文章没有编译。

如果没有事件类的定义,我没有一个确切的答案,但是在大学的例子中,我能够用本文撰写时的最新版本monocle 1.5.0-cats-M1更新嵌套地图。确保在您的项目中同时具有单片眼镜核心和单片眼镜宏JAR。那么

import monocle.macros.GenLens
import monocle.function.At.at // // to get at Lens 
import monocle.std.map._      // to get Map instance for At
然后以大学为例,

case class Lecturer(firstName: String, lastName: String, salary: Int)
case class Department(budget: Int, lecturers: List[Lecturer])
case class University(name: String, departments: Map[String, Department])

val departments = GenLens[University](_.departments) 

val uni = University("oxford", Map(
"Computer Science" -> Department(45, List(
  Lecturer("john"  , "doe", 10),
  Lecturer("robert", "johnson", 16)
)),
"History" -> Department(30, List(
  Lecturer("arnold", "stones", 20)
)))) 
我能

(departments composeLens at("History")).set(Some(Department(30, List(Lecturer("arnold", "stones", 30)))))(uni)

与上面代码的主要区别在于使用at()和将Department包装成与使用键从映射检索值进行访问时的选项返回类型相对应的部分

考虑到
someLabels
属于
Map[String,String]
类型,您的代码要么过多,要么只是为组合的
可选
提供了错误的参数。如果我们简化
Lens[S,A]
composeOptional
方法的签名,它将产生:

def composeOptional(other: Optional[A, B]): Optional[S, B]
可选[A,B]
在非常不精确的近似值下,对应于允许以下操作的间接寻址:

  • 查看类型
    A
    的值,并获取其类型
    B
    的组件(或者如果缺少
    A
    本身)
  • 通过替换
    B
    类型的组件来构建
    a
    类型的新对象(如果没有此类组件,则只返回原始对象)
标签组合可选索引(“app”)
产生
可选[元数据,字符串]
。这显然不适用于
Map[String,String]
:它从
Metadata
Map[String,String]
(通过
labels
),然后立即从
Map[String,String]
到它的
String
元素(通过
index(“app”)
),完全隐藏对用户的地图访问。如果您试图在
someLabels
map中的给定键处设置一个值,那么使用
index
就足够了:

val someLabels1 = Map("app" -> "any")
val someLabels2 = Map("unit" -> "any")
index("app").set("whatever")(someLabels1) // Map("app" -> "whatever")
index("app").set("whatever")(someLabels2) // Map("unit" -> "any")
另一方面,您编写的
可选
,可用于
元数据

case class Metadata(labels: Map[String, String])
val someLabels = Map("app" -> "any")
val meta = Metadata(someLabels)
(labels composeOptional index("app")).set("whatever")(meta) 
// Metadata(Map("app" -> "whatever")
我已经用以下版本(在
build.sbt
中)检查了它:


什么是
someLabels
?合成镜头是从
元数据
到索引
“app”
处的
标签
映射元素的间接指向,因此
一些标签
应该是
元数据
@P.Frolov类型。这是一个
映射[String,String]
我使用quicklens,因为我的使用量非常少,不值得费力地解决这些问题。单片眼镜还带来了很多依赖性(Scalaz、Shapess),这对我来说又是过分了。我以后再玩,如果你的回答对我有效,我就接受你的回答。在那之前,请考虑将投票作为赞赏的象征。AbjjiTrackar只有MunoCLE通用模块依赖于无形状的,我不认为您使用它。Scalaz或cats是core中唯一的依赖项,如果您想在scala中执行fp,它是必需的。
scalaVersion := 2.12.3

libraryDependencies ++= Seq(
  "com.github.julien-truffaut" %% "monocle-core" % "1.4.0",
  "com.github.julien-truffaut" %% "monocle-macro" % "1.4.0"
)