Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • May 13 22:10
    xuwei-k commented #1282
  • May 13 22:10
    xuwei-k closed #1282
  • May 13 22:10
    xuwei-k commented #1282
  • May 13 20:11
    scala-steward review_requested #1282
  • May 13 20:11
    scala-steward review_requested #1282
  • May 13 20:11
    scala-steward review_requested #1282
  • May 13 20:11
    scala-steward opened #1282
  • May 10 14:04

    julien-truffaut on gh-pages

    Deploy website Deploy website … (compare)

  • May 10 13:59

    xuwei-k on master

    Update refined, refined-scalach… (compare)

  • May 10 13:59
    xuwei-k closed #1281
  • May 10 13:55
    xuwei-k auto_merge_enabled #1281
  • May 10 13:49
    scala-steward review_requested #1281
  • May 10 13:49
    scala-steward review_requested #1281
  • May 10 13:49
    scala-steward review_requested #1281
  • May 10 13:49
    scala-steward opened #1281
  • Apr 21 03:05

    julien-truffaut on gh-pages

    Deploy website Deploy website … (compare)

  • Apr 21 03:00

    xuwei-k on master

    Update scalafmt-core to 3.5.2 (compare)

  • Apr 21 03:00
    xuwei-k closed #1280
  • Apr 21 00:33
    scala-steward review_requested #1280
  • Apr 21 00:33
    scala-steward review_requested #1280
Julien Truffaut
@julien-truffaut
Monocle 3.0.0-RC2 is released with a new organisation (dev.optics)
https://twitter.com/JulienTruffaut/status/1399663531772022785
fromscalatohaskell
@fromscalatohaskell_gitlab
fantastic
Igal Tabachnik
@hmemcpy

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...
Julien Truffaut
@julien-truffaut
you can improve the setting part in monocle 3 with n => p.focus(_.name).replace(n)
not shorter though
instead of doing fold maybe you could use foldMapM or other variants of foldMap
it is mostly to get rid of identity
Igal Tabachnik
@hmemcpy
Thanks! I was told that Chimney handles this exact scenario! https://scalalandio.github.io/chimney/patchers/options-handling.html
Julien Truffaut
@julien-truffaut
Nice
Igal Tabachnik
@hmemcpy
But I wonder if this could also be a good fit for an optics library.
Shah Saraei
@ShahOdin

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?

Julien Truffaut
@julien-truffaut
in Scala 3, you can do _.focus(_.bar1.some.xxx)
in Scala 2, you can do _.focus(_.bar1).some.andThen(Focus[Bar1](_.xxx))
Shah Saraei
@ShahOdin

Thanks Julien. I think you misunderstood my issue: I don't care about the nested types in BarXtypes 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

Julien Truffaut
@julien-truffaut
ah then, you can do
foo
  .focus(_.bar1).some.replace(maybeBar1)
  .focus(_.bar2).some.replace(maybeBar2)
Shah Saraei
@ShahOdin

tried above with Scala 2, I get:

found : Option[Bar1]
required: Bar1
.focus(.bar1).some.replace(maybeBar1)

Julien Truffaut
@julien-truffaut
maybeBar1 needs to be a Bar1
or do you want to set it to None if it is a Some?
Shah Saraei
@ShahOdin
see replaceIfSome impl above. I want to leave Foo alone if maybeBar is None, but replace its bar1 value if it's Some(_)
Julien Truffaut
@julien-truffaut
ah ok, you're right. I thought the if None / if Some logic was based on the field of Foo, but it is in fact based on maybeBar1
I should read more carefully next time
Shah Saraei
@ShahOdin
;) I guess it makes sense for replaceIfSome not to be hard-coded in the lib itself since as you say, the behaviour is not based on the Foo itself.
Julien Truffaut
@julien-truffaut
indeed
Ken Scambler
@kenbot
replace already only sets the value if it is present - this is why we renamed it from set, "replace" is supposed to suggest there's already a value
I'm not sure if you want us to second-guess your structure (sorry if not!), but a case class with fully optional members is often better represented by a Map
Shah Saraei
@ShahOdin

@julien-truffaut I have the same problem as here: https://stackoverflow.com/a/42668525/9199853 I tried to replicate your answer with the updated API:

  import monocle.macros.GenIso
  case class Alpha(text: String = "content")
  case class Beta(alpha: Alpha = Alpha())
  case class Gamma(beta: Option[Beta])
  val navigateToText = GenIso[Gamma, Option[Beta]]
    .andThen(GenIso[Beta, Alpha].asPrism.below[Option])
    .andThen(GenIso[Alpha, String].asPrism.below[Option])

it compiles. but if I add another field to Gamma, say: maybeI: Option[Int] it complains of:

Found several case class accessor for Gamma, Gamma needs to be a case class with a single accessor.

which makes sense now that I think about it. is there a way of solving the more generalised problem?

Shah Saraei
@ShahOdin
ok nvm, in general Beta might have multiple fields and with Gamme(None) it's not clear how we can create a Beta out of the blue which happens to have a nested Alpha. are there abstractions here that we could use to solve this problem?
Jan Bracker
@jbracker
What is the right monocle abstraction for an optional extractor aka "S => Option[A]" (an unapply method)? At first I thought I wanted a Prism or an Optional but both of those require a "reverse" route which I don't have. What I need is a Getter that delivers Option[A] instead of A. But if I use Getter[S, Option[A]] I need to flatten all of my results. Or is this something I should do with a special kind of Fold? I am unsure how the Monoid would work in that case...
Jan Bracker
@jbracker
Oh, I suppose this should work? override def foldMap[M](f: A => M)(s: S)(implicit monoid: Monoid[M]): M = unapply(s).fold(monoid.empty)(f)where unapply: S => Option[A]
Jan Bracker
@jbracker
Why are there no FoldLaws?
Julien Truffaut
@julien-truffaut
Foldable as no laws, I guess it could have some laws to check consistency between methods but that's it
Julien Truffaut
@julien-truffaut
felher
@felher

Hey folks :)

I'm trying to use monocle 3.0.0 with ScalaJS and Scala 3.0, but I'm getting Referring to non-existent class monocle.syntax.AppliedPLens$ as an error during fastLinkJS. Has anyone seen that before?

Julien Truffaut
@julien-truffaut
hey, could it be possible you have multiple monocle version in your path? (we renamed the organsiation)
felher
@felher
@julien-truffaut Sorry for the noise, I was just being dumb :)
But, for posterity, if someone makes the same mistake I did: Make sure, when using scalajs, that you have three, not two %%% (goes without saying, but I somehow missed it)
Julien Truffaut
@julien-truffaut
np :)
felher
@felher
Hey again ;)
Can I use focus to zoom into an index of list? Like person.focus(_.addresses(0)) ?
Wow, again. I found it... index. Sorry for the noise again. I have to be careful that doesn't become a running gag.
Ken Scambler
@kenbot
No worries, ask away! I'm glad it was discoverable
Hartmut Pfarr
@hartmut27
I use e.g. GenLens[MyType](_.abc.def) to build a lens for later usage, which works very very nice (Monocle 2.1.0 Scala 2.13).
In rare cases where e.g. a lens is passed to a function as a free parameter I would like I could sometimes additionally build a lens with "identity semantics": A lens which reflects the original object applied to the lens itself (and also a lens which does not change the optics when chained), using composeLens with a lens and "identity lenses" I would expect no effect on the result after executing the "lens chain".
I've tried GenLens[MyType](_) without success, GenLens[MyType](identity) does not compile, GenLens[MyType](x=>x) does not compile too.
Any hints? Eventually I've overseen something.
Julien Truffaut
@julien-truffaut
You can get an identity optics using Iso.id or Focus()
Hartmut Pfarr
@hartmut27
merci :) I'll check that
Julien Truffaut
@julien-truffaut
np
Actually in Moncole before 3.0.0, it must be Lens.id
Hartmut Pfarr
@hartmut27
ah I see
works perfectly :) :)
Matt Hughes
@matthughes

Upgrading to 3.0.0 and trying to switch to andThen but it’s causing ambiguous overloaded method in a lot of places.

                val x: Optional[State,Option[EiaChannelNumber]] = Lenses.muxEiaChannel(idx)
                val y: Optional[State,EiaChannelNumber] = x.andThen(some)
                // Overloaded method andThen 
                // Lenses.muxEiaChannel(idx).andThen(some)

It works if I explicitly type the intermediate variables but it does not like to be chained. Any tips?

Ken Scambler
@kenbot
By moving to a single overloaded compose method, we traded away some type inference for convenience. To mitigate this, there are now extension methods for common generic operations like each, some, index, at, etc
You should be able to do x.some