2015-11-26 2 views
0

Учитывая проект JsonExample в проекте монокля, я хотел бы создать объектив, в котором заданный вызов либо заменит значение в паре ключ/значение, либо создаст пару ключ/значение если он еще не существует.Scala: как повысить значение поля в Monocle

Однако это, кажется, представляется либо с индексом (который может сочинить типа сейфа) или на, который не типобезопасен

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

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

Это то, что я после того, как это возможно?
Также я мог продлить его, таким образом, что если возраст был вложен в другой JSObject, например:

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

Если пара ключа/значения для «вложенного» техника его подводит еще существует, что бы создать объект в вложенном а затем добавить поле

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

Было бы неплохо, если бы вы также указали свои определения 'jsObject' и' jsNumber' – Odomontois

+0

И экземпляр 'Index' – Odomontois

+0

Похоже, проблема заключается в реализации' compose ... 'в необязательном типе. Он обрабатывается методом 'modify'. Который отбрасывает внутренний набор, если оригинал «Нет» – Odomontois

ответ

4

давайте посмотрим на index и at подписи на JsObject:

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

at - Lens, поэтому его цель (опция [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.

+0

Итак, я закончил с помощью at (также у меня есть функция для удаления поля при установке (None). Для поддержания безопасности типа я просто использовал несложенную призму .set (prism.reverseGet (_)) –

2

на данный момент существует очень specific behaviour в библиотеке.

При составлении Optional с некоторыми Iso или Prism это понижение правой части аргумент POptional и неприятности.

Iso[A,B] и Prism[A,B] отличаются от Lens[A,B] и Optional[A,B] в смысле, что reverseGet может создать элемент A полностью из B в то время как set потребности первоначальной стоимости A

Итак, когда для Optional и Lens это абсолютно законно, что вы не можете изменить часть от значения, не имея этого значения в orignal 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) 

и сообщить, если он помог

+0

Это сработало как очарование, спасибо –

+0

@JPullar На самом деле я настоятельно рекомендую принять ответ Жюльена Трюффо. Сразу после написания этого ответа я прочитал объяснение Jullien здесь и в вопросе и пришел к выводу, что мой подход неверен. Принятие этого ответа может привести к непониманию идеологии библиотеки. – Odomontois

Смежные вопросы