Scala:如何在 Monocle 中插入字段值

Scala: how to upsert field value in Monocle

鉴于单片眼镜项目中的 JsonExample,我想创建一个镜头,其中 set 调用将替换 key/value 对中的值,或创建 key/value配对(如果尚不存在)。

然而,这似乎是用索引(可以组成安全类型)或 at 来表示的,它不是安全类型

//for replacing:
(jsObject composeOptional index("age") composePrism jsNumber).set(45)

//for creating:
(jsObject composeLens at("age")).set(JsNumber(45)) <- will accept any old json

我所追求的可能吗?
我也可以扩展它,这样如果 age 嵌套在另一个 JsObject 中,例如:

val n = (jsObject composeOptional index("nested") composePrism 
jsObject composeOptional index("age") composePrism jsNumber).set(45)

"nested" 的 key/value 对尚不存在,它将在嵌套处创建对象,然后添加字段

n(JsObject(Map.empty)) -> JsObject(Map("nested" -> JsObject("age" -> JsNumber(45)))

目前图书馆里有很多specific behaviour

当您将 Optional 与一些 IsoPrism 组合在一起时,它会将右侧参数降级为 POptional,这很麻烦。

Iso[A,B]Prism[A,B]Lens[A,B]Optional[A,B] 的不同之处在于 reverseGet 可以完全从 [= 创建 A 的元素22=] 而 set 需要 A

的原始值

所以当 OptionalLens 时,你不能修改部分值是完全合法的,而在原始 MapJsObject 中没有这个值, 对于 IsoPrism 你可以定义另一种行为。

在等待 the issue 讨论期间,您可以使用以下解决方法

implicit class POptStrictComposition[S, T, A, B](self: POptional[S, T, A, B]) {
  def sComposePrism[C, D](other: PPrism[A, B, C, D]) = new POptional[S, T, C, D] {
    def getOrModify(s: S): T \/ C =
      self.getOrModify(s).flatMap(a => other.getOrModify(a).bimap(self.set(_)(s), identity))

    def set(d: D): S => T =
      self.set(other.reverseGet(d))

    def getOption(s: S): Option[C] =
      self.getOption(s) flatMap other.getOption

    def modifyF[F[_] : Applicative](f: C => F[D])(s: S): F[T] =
      self.modifyF(other.modifyF(f))(s)

    def modify(f: C => D): S => T =
      self.modify(other.modify(f))
  }

  def ^!<-?[C, D](o: PPrism[A, B, C, D]) = sComposePrism(o)

  def sComposeIso[C, D](other: PIso[A, B, C, D]) = sComposePrism(other.asPrism)

  def ^!<->[C, D](o: PIso[A, B, C, D]) = sComposeIso(o)
}

现在您可以尝试将密码更改为

(jsObject composeOptional index("age") sComposePrism jsNumber).set(45)

如果有帮助请报告

让我们看看 indexat 签名 JsObject:

def at(field: String): Lens[JsObject, Option[Json]]
def index(field: String): Optional[JsObject, Json]

at 是一个 Lens,因此它的目标 ('Option[Json]') 始终存在。这意味着我们可以 adddeleteupdate Json 元素在 JsonObject.

的任何字段
import argonaut._, Argonaut._
import monocle.function._

(jObjectPrism composeLens at("name")).set(Some(jString("John")))(Json())
> res0: argonaut.Json = {"name":"John"}

(jObjectPrism composeLens at("name")).set(Some(jString("Robert")))(res0)
> res1: argonaut.Json = {"name":"Robert"}

(jObjectPrism composeLens at("name")).set(None)(res0)
> res2: argonaut.Json = {}

另一方面,index 是一个 Optional,因此目标 (Json) 可能存在也可能不存在。这意味着 index 只能 update 值但不能 adddelete.

(jObjectPrism composeLens index("name")).set(jString("Robert"))(Json())
> res3: argonaut.Json = {}

(jObjectPrism composeLens index("name")).set(jString("Robert"))(res0)
> res4: argonaut.Json = {"name":"Robert"}

所以回到你原来的问题,如果你想 addupdate 特定字段的值,你需要使用 at 并包装 JsonSome(参见 res1)中,它将覆盖或创建该字段的 Json