julien-truffaut on gh-pages
Deploy website Deploy website … (compare)
xuwei-k on master
Update sbt-mdoc to 2.3.3 (#1292) (compare)
julien-truffaut on gh-pages
Deploy website Deploy website … (compare)
xuwei-k on master
Update sbt to 1.7.1 (compare)
@Lenses
working on 2.13.6?
.field
(optics-dev/Monocle#1125) but it can be added later
I am trying to create a lens, which accesses different fields in a case class, eg: GenLens[Person](_.details.age)
, GenLens[Person](_.details.address)
, GenLens[Person](_.details.name)
Is there a way I can do this by generalizing the changing field? Something like
val attribute = "name"
GenLens[Person](_.details.$attribute)
I'm really stuck on something ... I'm trying to generate at runtime list of lenses to modify a data structure... here's simplified example:
sealed trait TreeLike
final case class Tree(id: String, leaves: List[Leaf]) extends TreeLike
final case class Leaf(i: Int) extends TreeLike
val tree = Tree(
"t1", List(Leaf(1), Leaf(2), Leaf(3))
)
val treeLens = tree.focus()
val lenses: List[AppliedLens[Tree, Leaf]] = ??? // list of lenses for leaves where i%2 ==0
how would I go about this? Where I essentially need to fold over runtime values to make lenses
btw kickass library
sealed trait TreeLike
final case class Tree(id: String, leaves: List[Leaf]) extends TreeLike
final case class Leaf(num: Int) extends TreeLike
val tree = Tree(
"t1", List(Leaf(1), Leaf(2), Leaf(3), Leaf(4))
)
val lenses: List[Traversal[Tree, Leaf]] =
tree.leaves.map {
x =>
if (x.num % 2 == 0) {
val ff = Focus[Tree](_.leaves)
.andThen(Traversal.fromTraverse[List, Leaf])
.andThen(
Prism[Leaf, Leaf] { bi =>
if (bi == x) Some(bi)
else None
}(identity)
)
Some(ff)
} else None
}.collect { case Some(x) => x }
val res = lenses.foldLeft(tree) { case (t, lens) =>
lens.modify(x => Leaf(x.num + 10))(t)
}
Traversal
(optics that targets many values of the same type), Plated
might also be an option, it deals with recursive data structures like your Tree https://github.com/optics-dev/Monocle/blob/master/example/src/test/scala/monocle/JsonExample.scala#L227-L259
Good evening all!
I was wondering how can I improve upon this manual thing I'm doing now?
Suppose I have a case class Person(name: String, age: Int)
. I receive a PATCH
request, containing optional fields that may have changed. What I'm currently doing is manually building a bunch of Person => Person
functions, either copying over the new value or returning the given person
unchanged, then applying all those "patch" functions one after the other to the initial person
.
Surely there's a better way :slight_smile:
case class PatchPersonRequest(name: Option[String], age: Option[Int])
val request = PatchPersonRequest(...)
val patchName: Person => Person = request.name.fold[Person => Person](identity)(n => p => p.copy(name = n))
val patchAge: Person => Person = request.age.fold[Person => Person](identity)(a => p => p.copy(age = a))
...
val updatedPerson = Patch.merge(
person,
patchName,
patchAge
...
)
I'm using monocle to update deeply nested properties of person
, but even then this is manual and repetitive... and boilerplatey :(
Patch.merge
does a foldLeft
over the Seq[A => A]
, applying it to the initial A
...
fold
maybe you could use foldMapM
or other variants of foldMap
identity
I have a product type with optional types such as
case class Foo(bar1: Option[Bar1], bar2: Option[Bar2], bar3: Option[Bar3])
I will have a list of optional bar[i]s and I want to optionally modify my Foo
instance with them.
as it stands I have to repeat _.focus.replace
for each param. is there a way of abstracting it out? I tried doing that with something like:
def updateWith[T](f: Foo => Option[T]): ???
but I get:
Illegal field reference f; please use _.field1.field2... instead
is there a way of marking updateWith
somehow to make this go away?
_.focus(_.bar1).some.andThen(Focus[Bar1](_.xxx))
Thanks Julien. I think you misunderstood my issue: I don't care about the nested types in BarX
types here. I have maybeValues which I would like to update my model with, if they were Some()
initially I wanted to do something like:
List(
maybeBar1.map(updateWith),
maybeBar2.map(updateWith)
).reduceLeft(..)
with the updateWith
being responsible for the fold
needed but then I faced the problem I described.
but I just realised I would be equally happy with something like:
foo
.focus(_.bar1).replaceIfSome(maybeBar1)
.focus(_.bar2).replaceIfSome(maybeBar2)
with the function looking something like:
implicit class AppliedLensOps[A, C](lens: AppliedLens[A, Option[C]]) {
def replaceIfSome(maybe: Option[C]): A = maybe.fold(
lens.value
)( w =>
lens.replace(w.some)
)
}
is there a more generic way of doing this? :thinking