Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement extractors #29

Open
dk14 opened this issue Jun 27, 2016 · 7 comments
Open

implement extractors #29

dk14 opened this issue Jun 27, 2016 · 7 comments

Comments

@dk14
Copy link

dk14 commented Jun 27, 2016

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
}
@adamw
Copy link
Member

adamw commented Jul 18, 2016

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?

@jedesah
Copy link

jedesah commented Oct 11, 2016

👍 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.

@adamw
Copy link
Member

adamw commented Oct 12, 2016

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?

@stanch
Copy link
Contributor

stanch commented Oct 12, 2016

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

@jedesah
Copy link

jedesah commented Oct 12, 2016

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.

@adamw
Copy link
Member

adamw commented Oct 13, 2016

@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)

@scalway
Copy link

scalway commented Apr 22, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants