quicklens icon indicating copy to clipboard operation
quicklens copied to clipboard

Compilation failure when using closure

Open bbdimitriu opened this issue 7 years ago • 11 comments

Using scala 2.12.4, JDK 8u141, Quicklens 1.4.11. Using the following setup with an example from the documentation:

case class Street(name: String)
case class Address(street: Option[Street])
case class Person(addresses: List[Address])

val person = Person(List(
            Address(Some(Street("1 Functional Rd."))),
            Address(Some(Street("2 Imperative Dr.")))
))

Then this will work:

person
     .modify(_.addresses.each.street.eachWhere(_.name.startsWith("1")).name)
     .using(_.toUpperCase)

but this will fail with a compilation error:

val one = "1"
person
     .modify(_.addresses.each.street.eachWhere(_.name.startsWith(one)).name)
     .using(_.toUpperCase)

The exception is quite long, but it starts like this:

[error] java.lang.IllegalArgumentException: Could not find proxy for val one: String in List(value one, method $anonfun$new$1, value <local QuickLensIncidentTest>, class QuickLensIncidentTest, package <empty>, package <root>) (currentOwner= method $anonfun$new$9 )
[error] 	at scala.tools.nsc.transform.LambdaLift$LambdaLifter.searchIn$1(LambdaLift.scala:310)
[error] 	at scala.tools.nsc.transform.LambdaLift$LambdaLifter.$anonfun$proxy$4(LambdaLift.scala:315)
[error] 	at scala.tools.nsc.transform.LambdaLift$LambdaLifter.searchIn$1(LambdaLift.scala:315)
[error] 	at scala.tools.nsc.transform.LambdaLift$LambdaLifter.$anonfun$proxy$4(LambdaLift.scala:315)
[error] 	at scala.tools.nsc.transform.LambdaLift$LambdaLifter.searchIn$1(LambdaLift.scala:315)
[error] 	at scala.tools.nsc.transform.LambdaLift$LambdaLifter.$anonfun$proxy$4(LambdaLift.scala:315)
[error] 	at scala.tools.nsc.transform.LambdaLift$LambdaLifter.searchIn$1(LambdaLift.scala:315)
[error] 	at scala.tools.nsc.transform.LambdaLift$LambdaLifter.$anonfun$proxy$4(LambdaLift.scala:315)

So it looks like closures don't work.

bbdimitriu avatar Dec 28 '17 15:12 bbdimitriu

Hi,

Same problem, but with a NoSuchElementException instead of a IllegalArgumentException.

The following code is ok:

case class B(n: Int)

val xs = Seq(B(1), B(2), B(3), B(4))
val ys = xs.modify(_.eachWhere(_.n % 2 == 0).n).using(_ * 2)

But this one is not:

case class B(n: Int)

val xs = Seq(B(1), B(2), B(3), B(4))
val test = 0
val ys = xs.modify(_.eachWhere(_.n % 2 == test).n).using(_ * 2)

The error is:

java.util.NoSuchElementException: value test
	at scala.collection.mutable.AnyRefMap$ExceptionDefault.apply(AnyRefMap.scala:425)
	at scala.collection.mutable.AnyRefMap$ExceptionDefault.apply(AnyRefMap.scala:424)
	at scala.collection.mutable.AnyRefMap.apply(AnyRefMap.scala:180)
	at scala.tools.nsc.backend.jvm.BCodeSkelBuilder$PlainSkelBuilder$locals$.load(BCodeSkelBuilder.scala:389)
	at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genLoad(BCodeBodyBuilder.scala:354)
	at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.$anonfun$genLoadArguments$1(BCodeBodyBuilder.scala:935)
	at scala.tools.nsc.backend.jvm.BCodeBodyBuilder$PlainBodyBuilder.genLoadArguments(BCodeBodyBuilder.scala:935)

PS: scala 2.12.4 and quicklens 1.4.11.

guyonvarch avatar Jan 16 '18 16:01 guyonvarch

As a workaround, I found out that you can rewrite

val one = "1"
person
     .modify(_.addresses.each.street.eachWhere(_.name.startsWith(one)).name)
     .using(_.toUpperCase)

to

val one = "1"
val w: Street => Boolean = _.name.startsWith(one)   // also works when 'one' comes from elsewhere
person
     .modify(_.addresses.each.street.eachWhere(w).name)
     .using(_.toUpperCase)

Not as nice, but at least it compiles.

erikvanoosten avatar Feb 08 '18 11:02 erikvanoosten

i ran into the same eachWhere issue with quicklens 1.4.11 and scala 2.12.7.

event match {
  case Bar(fooId, status) =>
    state.modify(_.bars.eachWhere(_.id == fooId).status).setTo(status)
}

however, i wasn't getting any exception; sbt simply failed with the cryptic:

[error] Error while emitting Foo.scala
[error] value fooId

mizerlou avatar Feb 14 '19 01:02 mizerlou

There is a workaround (see @erikvanoosten post):

event match {
  case Bar(fooId, status) =>
    val condition: Bar => Boolean = _.id == fooId
    state.modify(_.bars.eachWhere(condition).status).setTo(status)
}

I wish if the original eachWhere worked.

akahanek avatar Jul 07 '19 08:07 akahanek

we stumbled across this bug as well after switching to 2.12. took ages to understand this was due to this very bug, but better late than never. the workaround does work. please let me know if I could help, I am willing to spend time on this fixing it. But would need at least some guidance

pete-proton avatar Mar 29 '22 08:03 pete-proton

@pete-proton I wish I could help you, but I'm not sure where to start as well :) Did you try on 2.13 - is the problem there as well? If not, this might indicate that this is some 2.12-compiler-specific hmm ... let's say "feature", that might be hard to overcome. If this happens also on 2.13, maybe we incorrectly use the tree api in the macro

adamw avatar Mar 29 '22 09:03 adamw

@adamw I see, I will try with 2.13 and see how that goes. thanks for the quick response

pete-proton avatar Mar 29 '22 09:03 pete-proton

Hi there. Is there any idea about when it would be available? @adamw I'm using the 2.13 version and I have the same problem.

emersonmoura avatar Mar 31 '22 00:03 emersonmoura

@pete-proton I wish I could help you, but I'm not sure where to start as well :) Did you try on 2.13 - is the problem there as well? If not, this might indicate that this is some 2.12-compiler-specific hmm ... let's say "feature", that might be hard to overcome. If this happens also on 2.13, maybe we incorrectly use the tree api in the macro

@adamw thank you for the guidance. the issue seems to be reproducible using scala 2.13, see. If you could give directions on how to fix it, I would be very grateful. I just have not worked with macros before. thank you in advance.

pete-proton avatar May 01 '22 06:05 pete-proton

@pete-proton If I only knew I would fix it :) I would start by looking at usages of FunctorPathElement in QuicklensMacros, but not sure if this will lead you anywhere ...

adamw avatar May 06 '22 13:05 adamw

thank you, @adamw

pete-proton avatar May 06 '22 14:05 pete-proton