quicklens icon indicating copy to clipboard operation
quicklens copied to clipboard

implement extractors

Open dk14 opened this issue 8 years ago • 7 comments

As we can't use:

scala> def extractor(p: Person) = p.address.street.name
extractor: (p: Person)String

scala> val p2 = person.modify(extractor).using(_.toUpperCase)
<console>:21: error: Path must have shape: _.field1.field2.each.field3.(...)

as it's hard to analyze such complex AST, it would be great to have something like:

person.modify(_.address.street.name).get

Which will return a List of values. This will allow to reuse a path for both read and write. My practical need is to actually check a path at one instance of case class, but modify the same path in another instance of the same case class:

val accessor = (_: Person).modify(_.address.street.name)  //"access" alias for "modify" would be also great

if(accessor(person1).get == "Functional Road") accessor(person2).setTo("Imperative Road")

Currently, I've got to hack it with vars (or alternate the model), which is not very functional...

def getter[T, U](pm: T => PathModify[T, U])(in: T): List[U] = {
   var values: List[U] = Nil //list is just in case of "each"
   pm(in).using{x =>
     values ++= x
     x
   }
   values
}

dk14 avatar Jun 27 '16 21:06 dk14

so in fact you want to create lenses, right? :)

You could have: (_: Person).lens(_.x.y.z).get, .setTo, .modifyUsing, what do you think? But maybe if you need lenses, https://github.com/julien-truffaut/Monocle already has what you need?

adamw avatar Jul 18 '16 15:07 adamw

:+1: for (_: Person).lens(_.x.y.z).get, .setTo, .modifyUsing

I also have a use case where that would be super convenient. Of course I could use Monocle, but I prefer the terse syntax of quicklens.

jedesah avatar Oct 11 '16 21:10 jedesah

There's one major problem here unfortunately: when you are using e.g. .each unwrapping. Then you wouldn't be able to get a single value (as there might be 0-many values).

A partial solution could be to constrain .lens no to allow .each unwrapping of lists and such (during compile-time), but then wouldn't the (now quite simple) API become complex?

adamw avatar Oct 12 '16 05:10 adamw

FWIW, in Monocle the things that can extract/modify several values at once (but not get a single value) are called Traversals.

stanch avatar Oct 12 '16 09:10 stanch

I think it's possible to come up with both a simple and powerful API here, but that might require making the implementation more complex.

More concretely, I would suggest that get return a list if .each was used at some point in the chain. After all, the return type does not need to be static since the API makes use of macros.

jedesah avatar Oct 12 '16 20:10 jedesah

@jedesah I would be against using whitebox macros, for two main reasons: they are not IDE-friendly (you don't know the return type until you actually run the compiler), and they won't be supported in the new scala-meta based macros (while you should be able to implement quicklens in its current form using scala meta)

adamw avatar Oct 13 '16 05:10 adamw

I'm using such code right now to have .get method

class QuckLensExt[T,U](private val pm: PathModify[T, U]) extends AnyVal {
  def get = {
    var values: List[U] = Nil
    pm.using { x =>
      values ::= x
      x
    }
    values
  }
}


even if introducing get is not an option it would be nice to have cheaper way of implementing this hack. For example pm.usingPartial { case x if {values ::= x; false} => x } just to not copy object unnecessarily.

scalway avatar Apr 22 '20 11:04 scalway