optics-dev / Monocle

Optics library for Scala
https://www.optics.dev/Monocle/
MIT License
1.65k stars 205 forks source link

How to set value with Option type? #215

Closed wpoosanguansit closed 9 years ago

wpoosanguansit commented 9 years ago

Hi,

I am having a similar situation as below:

@Lenses("") case class Object5(v4: Option[String], v5: Int) @Lenses("") case class Object3(object4: Option[Object5], v3: Option[String]) @Lenses("") case class Object2(v: String, v2: String) @Lenses("") case class Object1(object2: Object2, object3: Option[Object3])

object Test { Object1._object3.^|->(Object3._v3).set(Option("test"))(object1) }

The question is how do I set the Object3._v3 which is an option type? Now I can set the object3 itself but I still do not know how to reference it down to the next level. Thanks for your help.

julien-truffaut commented 9 years ago

you can use some: Prism[Option[A], A] defined in monocle.std.option, e.g.:

import monocle.std.option._
(_object3 composePrism some).set("test")(object1)
wpoosanguansit commented 9 years ago

Thanks for your help. I tried to follow the example with the following code @Lenses("") case class Object5(v4: Option[String], v5: Int) @Lenses("") case class Object3(object4: Option[Object5], v3: Option[String]) @Lenses("") case class Object2(v: String, v2: String) @Lenses("") case class Object1(object2: Object2, object3: Option[Object3])

object Test { import monocle.std.option._ val object3 = Object3(None, None) val object1 = Object1(Object2("", ""), None) (Object1._object3 composePrism some).set("test")(object1) }

But there is still an error on set which would require Object3 and not string. I think I am still missing something.

julien-truffaut commented 9 years ago

ah sorry I didn't notice that they were another nested level, can you try this:

(Object1._object3 composePrism some composeLens Object3._v3 composePrism some).set("test")(object1)
wpoosanguansit commented 9 years ago

Thanks for your help. That works!

kthompson commented 7 years ago

Hello. I am also trying to use the some prism and maybe I am doing something wrong but what I have is not working.

@Lenses final case class TestClass(a: Option[Int])
val lens = TestClass.a.composePrism(some)

val res = lens.set(5)(TestClass(None))
res.a should contain(5)

My unit tests says None did not contain element 5. Am I doing this right?

Just to add to this I am using an old version in case that matters significantly(1.2.2).

julien-truffaut commented 7 years ago

@kthompson that's the principle of Prism, it only succeeds to set value if it is matching, i.e. with some you can only set a value in an Option that's a Some.

Here are Prism laws: https://github.com/julien-truffaut/Monocle/blob/master/core/shared/src/main/scala/monocle/law/PrismLaws.scala#L16-L20

You can also have a look at Prism doc to understand more how does it work: http://julien-truffaut.github.io/Monocle/optics/prism.html

ryan-richt commented 3 years ago

Since this is the top search engine hit for monocle + option, maybe this belongs here...

Is it possible to create a single optic that, in the case of the target being a Some, we modify using a function, and in the case of a None, we set a different thing? Basically the equivalent of myOption.fold(ifEmpty)(ifSome => newSome) from the Scala standard library. I see some pieces ... choice / split / optionToDisjunction but can't find any examples of their use or docs on the internets.

Thank you for the essential library @julien-truffaut !

julien-truffaut commented 3 years ago

Hi @ryan-richt

Yes, we added a new method on all optics called withDefault that allows to specify a default value to use in case the Option is a None.

Here is a test example: https://github.com/optics-dev/Monocle/blob/master/test/shared/src/test/scala/monocle/TraversalSpec.scala#L172-L175

And here is some documentation related to this optic: https://github.com/optics-dev/Monocle/blob/master/core/shared/src/main/scala/monocle/std/Option.scala#L25-L43

Unfortunately, this is not yet released. We should include it in the next milestone release in a week or two.