optics-dev / Monocle

Optics library for Scala

Home Page:https://www.optics.dev/Monocle/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Focus type can't be inferred on this in a class member

kenbot opened this issue · comments

Standalone this works:

case class Foo(str: String)
val foo = Foo("abc")
def strLens = foo.focus(_.str) // Correctly infers return type as AppliedLens[Foo, String]

// Also: 
class Banana: 
  def blah = foo.focus(_.str) // Correctly infers member return type as AppliedLens[Foo, String]

However it fails when the focus is on this:

case class Foo(str: String):
  def strLens = this.focus(_.str) // Does not compile: dotty.tools.dotc.core.CyclicReference

/////////////
Exception occurred while executing macro expansion.
[error]    |dotty.tools.dotc.core.CyclicReference:
[error]    |    at dotty.tools.dotc.core.CyclicReference$.apply(TypeErrors.scala:155)
......
|    at monocle.internal.focus.features.SelectParserBase.getTrimmedFieldSymbol$3(SelectParserBase.scala:34)
[error]    |    at monocle.internal.focus.features.SelectParserBase.getFieldType$$anonfun$1(SelectParserBase.scala:37)
[error]    |    at scala.util.Either.flatMap(Either.scala:352)
[error]    |    at monocle.internal.focus.features.SelectParserBase.getFieldType(SelectParserBase.scala:40)
[error]    |    at monocle.internal.focus.features.SelectParserBase.getFieldType$(SelectParserBase.scala:5)
[error]    |    at monocle.internal.focus.FocusImpl.getFieldType(FocusImpl.scala:7)
[error]    |    at monocle.internal.focus.features.selectonlyfield.SelectOnlyFieldParser.monocle$internal$focus$features$selectonlyfield$SelectOnlyFieldParser$$getFieldAction(SelectOnlyFieldParser.scala:27)
[error]    |    at monocle.internal.focus.features.selectonlyfield.SelectOnlyFieldParser$SelectOnlyField$.unapply(SelectOnlyFieldParser.scala:17)
[error]    |    at monocle.internal.focus.features.ParserLoop.loop$1(ParserLoop.scala:57)
[error]    |    at monocle.internal.focus.features.ParserLoop.parseFocusActions(ParserLoop.scala:66)
[error]    |    at monocle.internal.focus.features.ParserLoop.parseFocusActions$(ParserLoop.scala:27)
[error]    |    at monocle.internal.focus.FocusImpl.parseFocusActions(FocusImpl.scala:7)
[error]    |    at monocle.internal.focus.FocusImpl.$anonfun$1(FocusImpl.scala:20)
[error]    |    at scala.util.Either.flatMap(Either.scala:352)
[error]    |    at monocle.internal.focus.FocusImpl.run(FocusImpl.scala:22)
[error]    |    at monocle.internal.focus.FocusImpl$.apply(FocusImpl.scala:33)
[error]    |    at monocle.internal.focus.AppliedFocusImpl$.apply(AppliedFocusImpl.scala:13)

It works with explicit return type:

case class Foo(str: String):
  def strLens: monocle.AppliedLens[Foo, String] = this.focus(_.str) // Compiles fine

This happens at

fromTypeSymbol.memberFields.find(_.name.trim == fieldName).getOrElse(Symbol.noSymbol)
; the call to memberFields ordinarily wouldn't need to revisit the currently-being-defined Focus expression, but the lack of an explicit return type means it needs to evaluate the method definition, which recurses into the Focus expr again.

No idea how to wriggle out of this yet!

I think this does need to be addressed, unfortunately, because assigning an optic to a def with an inferred type is a completely reasonable thing for a user to attempt, and the user experience of seeing incomprehensible compiler vomit is unacceptable. Even if we can intercept the attempt and give a nice error message guiding the user to explicitly write out the type, that would be fine I think.

FYI: This was fixed in Scala 3.0.2, most likely via pull-request 12873.

Oh great! Confirmed on 3.0.2. Thanks for the heads up @NTPape. My favourite kind of fix. 😀