1

Given the JsonExample in the monocle project, I would like to create a lens where a set call will either replace a value in a key/value pair, or create the key/value pair if it doesnt already exist.

However this seems to represented with either an index (which can compose type safe) or an at, which does not type safe

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

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

Is what I am after possible?
Also could I extend it, such that if age was nested in another JsObject, for example:

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

Where the key/value pair for "nested" didnt yet exist, that it would create the object at nested and then add the field

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

2 回答 2

5

让我们看看indexat签名JsObject

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

at是一个Lens所以它的目标 ('Option[Json]') 总是存在的。这意味着我们可以adddelete并且updateJson元素位于 a 的任何字段中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 = {}

另一方面,indexis anOptional所以它的 target ( Json) 可能存在也可能不存在。这意味着indexcan onlyupdate值但不能addor 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"}

所以回到你原来的问题,如果你想在一个特定的领域addupdate价值,你需要使用at和包装Json在一个Some(见res1),它会覆盖或创建Json在那个领域。

于 2015-11-26T13:44:17.000 回答
3

目前,图书馆中有非常具体的行为

当您Optional与 someIsoPrism它正在将右手边的论点降级时POptional,这很麻烦。

Iso[A,B]Prism[A,B]不同Lens[A,B]Optional[A,B]在某种意义上说,可以完全从reverseGet创造元素,而需要原始价值ABsetA

因此,当 for 时OptionalLens您不能修改部分值是完全合法的,而在 orignal 中没有此值MapJsObjectforIso并且Prism您可以定义另一种行为。

在等待讨论问题时,您可以使用以下解决方法

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)

并报告它是否有帮助

于 2015-11-26T12:49:08.943 回答