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
与一些 Iso
或 Prism
组合在一起时,它会将右侧参数降级为 POptional
,这很麻烦。
Iso[A,B]
和 Prism[A,B]
与 Lens[A,B]
和 Optional[A,B]
的不同之处在于 reverseGet
可以完全从 [= 创建 A
的元素22=] 而 set
需要 A
的原始值
所以当 Optional
和 Lens
时,你不能修改部分值是完全合法的,而在原始 Map
或 JsObject
中没有这个值, 对于 Iso
和 Prism
你可以定义另一种行为。
在等待 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)
如果有帮助请报告
让我们看看 index
和 at
签名 JsObject
:
def at(field: String): Lens[JsObject, Option[Json]]
def index(field: String): Optional[JsObject, Json]
at
是一个 Lens
,因此它的目标 ('Option[Json]') 始终存在。这意味着我们可以 add
、delete
和 update
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
值但不能 add
或 delete
.
(jObjectPrism composeLens index("name")).set(jString("Robert"))(Json())
> res3: argonaut.Json = {}
(jObjectPrism composeLens index("name")).set(jString("Robert"))(res0)
> res4: argonaut.Json = {"name":"Robert"}
所以回到你原来的问题,如果你想 add
或 update
特定字段的值,你需要使用 at
并包装 Json
在 Some
(参见 res1
)中,它将覆盖或创建该字段的 Json
。
鉴于单片眼镜项目中的 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
与一些 Iso
或 Prism
组合在一起时,它会将右侧参数降级为 POptional
,这很麻烦。
Iso[A,B]
和 Prism[A,B]
与 Lens[A,B]
和 Optional[A,B]
的不同之处在于 reverseGet
可以完全从 [= 创建 A
的元素22=] 而 set
需要 A
所以当 Optional
和 Lens
时,你不能修改部分值是完全合法的,而在原始 Map
或 JsObject
中没有这个值, 对于 Iso
和 Prism
你可以定义另一种行为。
在等待 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)
如果有帮助请报告
让我们看看 index
和 at
签名 JsObject
:
def at(field: String): Lens[JsObject, Option[Json]]
def index(field: String): Optional[JsObject, Json]
at
是一个 Lens
,因此它的目标 ('Option[Json]') 始终存在。这意味着我们可以 add
、delete
和 update
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
值但不能 add
或 delete
.
(jObjectPrism composeLens index("name")).set(jString("Robert"))(Json())
> res3: argonaut.Json = {}
(jObjectPrism composeLens index("name")).set(jString("Robert"))(res0)
> res4: argonaut.Json = {"name":"Robert"}
所以回到你原来的问题,如果你想 add
或 update
特定字段的值,你需要使用 at
并包装 Json
在 Some
(参见 res1
)中,它将覆盖或创建该字段的 Json
。