Skip to content

Commit 5d0d6cc

Browse files
committed
Move eta expansion / implicit search into parser
1 parent d37be02 commit 5d0d6cc

9 files changed

Lines changed: 117 additions & 77 deletions

File tree

core/shared/src/main/scala-3.x/monocle/internal/focus/ErrorHandling.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,9 @@ private[focus] trait ErrorHandling {
1717
case FocusError.UnexpectedCodeStructure(code) => s"Unexpected code structure: $code"
1818
case FocusError.CouldntFindFieldType(fromType, fieldName) => s"Couldn't find type for $fromType.$fieldName"
1919
case FocusError.InvalidDowncast(fromType, toType) => s"Type '$fromType' could not be cast to '$toType'"
20+
case FocusError.ImplicitNotFound(implicitType) =>
21+
s"Could not find implicit for '$implicitType'. Note: multiple non-implicit parameter sets or implicits with default values are not supported."
22+
case FocusError.ExpansionFailed(reason) =>
23+
s"Case class with multiple parameter sets could not be expanded because of: $reason"
2024
}
2125
}

core/shared/src/main/scala-3.x/monocle/internal/focus/FocusBase.scala

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@ private[focus] trait FocusBase {
1313
case class LambdaConfig(argName: String, lambdaBody: Term)
1414

1515
enum FocusAction {
16-
case SelectField(fieldName: String, fromType: TypeRepr, fromTypeArgs: List[TypeRepr], toType: TypeRepr)
16+
case SelectField(
17+
fieldName: String,
18+
fromType: TypeRepr,
19+
toType: TypeRepr,
20+
setter: Term
21+
)
1722
case SelectOnlyField(
1823
fieldName: String,
1924
fromType: TypeRepr,
20-
fromTypeArgs: List[TypeRepr],
21-
fromCompanion: Term,
22-
toType: TypeRepr
25+
toType: TypeRepr,
26+
reverseGet: Term
2327
)
2428
case KeywordSome(toType: TypeRepr)
2529
case KeywordAs(fromType: TypeRepr, toType: TypeRepr)
@@ -29,10 +33,10 @@ private[focus] trait FocusBase {
2933
case KeywordWithDefault(toType: TypeRepr, defaultValue: Term)
3034

3135
override def toString(): String = this match {
32-
case SelectField(fieldName, fromType, fromTypeArgs, toType) =>
33-
s"SelectField($fieldName, ${fromType.show}, ${fromTypeArgs.map(_.show)}, ${toType.show})"
34-
case SelectOnlyField(fieldName, fromType, fromTypeArgs, _, toType) =>
35-
s"SelectOnlyField($fieldName, ${fromType.show}, ${fromTypeArgs.map(_.show)}, ..., ${toType.show})"
36+
case SelectField(fieldName, fromType, toType, setter) =>
37+
s"SelectField($fieldName, ${fromType.show}, ${toType.show}, ${setter.asExpr.show})"
38+
case SelectOnlyField(fieldName, fromType, toType, reverseGet) =>
39+
s"SelectOnlyField($fieldName, ${fromType.show}, ${toType.show}, ${reverseGet.asExpr.show})"
3640
case KeywordSome(toType) => s"KeywordSome(${toType.show})"
3741
case KeywordAs(fromType, toType) => s"KeywordAs(${fromType.show}, ${toType.show})"
3842
case KeywordEach(fromType, toType, _) => s"KeywordEach(${fromType.show}, ${toType.show}, ...)"
@@ -52,6 +56,8 @@ private[focus] trait FocusBase {
5256
case CouldntFindFieldType(fromType: String, fieldName: String)
5357
case ComposeMismatch(type1: String, type2: String)
5458
case InvalidDowncast(fromType: String, toType: String)
59+
case ImplicitNotFound(implicitType: String)
60+
case ExpansionFailed(reason: String)
5561

5662
def asResult: FocusResult[Nothing] = Left(this)
5763
}

core/shared/src/main/scala-3.x/monocle/internal/focus/features/GeneratorLoop.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import scala.quoted.Type
1414

1515
private[focus] trait AllFeatureGenerators
1616
extends FocusBase
17-
with SelectGeneratorBase
1817
with SelectFieldGenerator
1918
with SelectOnlyFieldGenerator
2019
with SomeGenerator

core/shared/src/main/scala-3.x/monocle/internal/focus/features/SelectGeneratorBase.scala

Lines changed: 0 additions & 47 deletions
This file was deleted.

core/shared/src/main/scala-3.x/monocle/internal/focus/features/SelectParserBase.scala

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package monocle.internal.focus.features
22

33
import monocle.internal.focus.FocusBase
4+
import scala.annotation.tailrec
5+
import scala.util.Failure
6+
import scala.util.Success
7+
import scala.util.Try
48

59
private[focus] trait SelectParserBase extends ParserBase {
610
this: FocusBase =>
@@ -30,7 +34,7 @@ private[focus] trait SelectParserBase extends ParserBase {
3034
// We need to do this to support tuples, because even though they conform as case classes in other respects,
3135
// for some reason their field names (_1, _2, etc) have a space at the end, ie `_1 `.
3236
def getTrimmedFieldSymbol(fromTypeSymbol: Symbol): Symbol =
33-
fromTypeSymbol.memberFields.find(_.name.trim == fieldName).getOrElse(Symbol.noSymbol)
37+
fromTypeSymbol.fieldMembers.find(_.name.trim == fieldName).getOrElse(Symbol.noSymbol)
3438

3539
getClassSymbol(fromType).flatMap { fromTypeSymbol =>
3640
getTrimmedFieldSymbol(fromTypeSymbol) match {
@@ -69,4 +73,36 @@ private[focus] trait SelectParserBase extends ParserBase {
6973
case Some(typeParamList :: _) if typeParamList.exists(_.isTypeParam) => typeParamList
7074
case _ => Nil
7175
}
76+
77+
@tailrec
78+
final def etaExpandIfNecessary(term: Term): FocusResult[Term] =
79+
if (term.isExpr) {
80+
Right(term)
81+
} else {
82+
val expanded: Term = term.etaExpand(Symbol.spliceOwner)
83+
84+
val implicitsResult: FocusResult[List[Term]] =
85+
expanded match {
86+
case Block(List(DefDef(_, List(params), _, _)), _) =>
87+
params.params.foldLeft[FocusResult[List[Term]]](Right(List.empty[Term])) {
88+
case (Right(acc), ValDef(_, t, _)) =>
89+
val typeRepr: TypeRepr = t.tpe.dealias
90+
Implicits.search(typeRepr) match {
91+
case success: ImplicitSearchSuccess => Right(success.tree :: acc)
92+
case _ => FocusError.ImplicitNotFound(typeRepr.show).asResult
93+
}
94+
case (Right(acc), other) =>
95+
FocusError.ExpansionFailed(s"Expected value definition but found unexpected ${other.show}").asResult
96+
case (left @ Left(_), _) =>
97+
left
98+
}
99+
case other =>
100+
FocusError.ExpansionFailed(s"Expected block of expanded term but found unexpected ${other.show}").asResult
101+
}
102+
103+
implicitsResult match {
104+
case Left(error) => Left(error)
105+
case Right(implicits) => etaExpandIfNecessary(Apply(term, implicits))
106+
}
107+
}
72108
}

core/shared/src/main/scala-3.x/monocle/internal/focus/features/selectfield/SelectFieldGenerator.scala

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
11
package monocle.internal.focus.features.selectfield
22

33
import monocle.internal.focus.FocusBase
4-
import monocle.internal.focus.features.SelectGeneratorBase
54
import monocle.Lens
65

76
private[focus] trait SelectFieldGenerator {
8-
this: FocusBase with SelectGeneratorBase =>
7+
this: FocusBase =>
98

109
import macroContext.reflect._
1110

1211
def generateSelectField(action: FocusAction.SelectField): Term = {
13-
import action.{fieldName, fromType, fromTypeArgs, toType}
14-
15-
def generateSetter(from: Term, to: Term): Term =
16-
// o.copy(field = value)(implicits)*
17-
etaExpandIfNecessary(Select.overloaded(from, "copy", fromTypeArgs, NamedArg(fieldName, to) :: Nil))
12+
import action.{fieldName, fromType, toType, setter}
1813

1914
(fromType.asType, toType.asType) match {
2015
case ('[f], '[t]) =>
2116
'{
22-
Lens.apply[f, t]((from: f) => ${ generateGetter('{ from }.asTerm, fieldName).asExprOf[t] })((to: t) =>
23-
(from: f) => ${ generateSetter('{ from }.asTerm, '{ to }.asTerm).asExprOf[f] }
17+
Lens.apply[f, t]((from: f) => ${ Select.unique('{ from }.asTerm, fieldName).asExprOf[t] })(
18+
${ setter.asExprOf[t => f => f] }
2419
)
2520
}.asTerm
2621
}

core/shared/src/main/scala-3.x/monocle/internal/focus/features/selectfield/SelectFieldParser.scala

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,33 @@ private[focus] trait SelectFieldParser {
2727

2828
private def getFieldAction(fromType: TypeRepr, fieldName: String): FocusResult[FocusAction] =
2929
getFieldType(fromType, fieldName).flatMap { toType =>
30-
Right(FocusAction.SelectField(fieldName, fromType, getSuppliedTypeArgs(fromType), toType))
30+
val typeArgs = getSuppliedTypeArgs(fromType)
31+
constructSetter(fieldName, fromType, toType, typeArgs).map { setter =>
32+
FocusAction.SelectField(fieldName, fromType, toType, setter)
33+
}
34+
}
35+
36+
private case class LiftException(error: FocusError) extends Exception
37+
38+
private def constructSetter(
39+
fieldName: String,
40+
fromType: TypeRepr,
41+
toType: TypeRepr,
42+
fromTypeArgs: List[TypeRepr]
43+
): FocusResult[Term] =
44+
// Companion.copy(value)(implicits)*
45+
(fromType.asType, toType.asType) match {
46+
case ('[f], '[t]) =>
47+
scala.util.Try('{ (to: t) => (from: f) =>
48+
${
49+
etaExpandIfNecessary(
50+
Select.overloaded('{ from }.asTerm, "copy", fromTypeArgs, List(NamedArg(fieldName, '{ to }.asTerm)))
51+
).fold(error => throw new LiftException(error), _.asExprOf[f])
52+
}
53+
}.asTerm) match {
54+
case scala.util.Success(term) => Right(term)
55+
case scala.util.Failure(LiftException(error)) => Left(error)
56+
case scala.util.Failure(other) => Left(FocusError.ExpansionFailed(other.toString))
57+
}
3158
}
3259
}

core/shared/src/main/scala-3.x/monocle/internal/focus/features/selectonlyfield/SelectOnlyFieldGenerator.scala

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
11
package monocle.internal.focus.features.selectonlyfield
22

33
import monocle.internal.focus.FocusBase
4-
import monocle.internal.focus.features.SelectGeneratorBase
54
import monocle.Iso
65

76
private[focus] trait SelectOnlyFieldGenerator {
8-
this: FocusBase with SelectGeneratorBase =>
7+
this: FocusBase =>
98

109
import macroContext.reflect._
1110

1211
def generateSelectOnlyField(action: FocusAction.SelectOnlyField): Term = {
13-
import action.{fieldName, fromType, fromTypeArgs, fromCompanion, toType}
14-
15-
def generateReverseGet(to: Term): Term =
16-
// Companion.apply(value)(implicits)*
17-
etaExpandIfNecessary(Select.overloaded(fromCompanion, "apply", fromTypeArgs, List(to)))
12+
import action.{fieldName, fromType, toType, reverseGet}
1813

1914
(fromType.asType, toType.asType) match {
2015
case ('[f], '[t]) =>
2116
'{
22-
Iso.apply[f, t]((from: f) => ${ generateGetter('{ from }.asTerm, fieldName).asExprOf[t] })((to: t) =>
23-
${ generateReverseGet('{ to }.asTerm).asExprOf[f] }
17+
Iso.apply[f, t]((from: f) => ${ Select.unique('{ from }.asTerm, fieldName).asExprOf[t] })(
18+
${ reverseGet.asExprOf[t => f] }
2419
)
2520
}.asTerm
2621
}

core/shared/src/main/scala-3.x/monocle/internal/focus/features/selectonlyfield/SelectOnlyFieldParser.scala

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,36 @@ private[focus] trait SelectOnlyFieldParser {
2727
toType <- getFieldType(fromType, fieldName)
2828
companion <- getCompanionObject(fromType)
2929
supplied = getSuppliedTypeArgs(fromType)
30-
} yield FocusAction.SelectOnlyField(fieldName, fromType, supplied, companion, toType)
30+
reverseGet <- constructReverseGet(companion, fromType, toType, supplied)
31+
} yield FocusAction.SelectOnlyField(fieldName, fromType, toType, reverseGet)
3132

3233
private def hasOnlyOneField(fromCode: Term): Boolean =
3334
getType(fromCode).classSymbol.exists(_.caseFields.length == 1)
3435

3536
private def getCompanionObject(fromType: TypeRepr): FocusResult[Term] =
3637
getClassSymbol(fromType).map(sym => Ref(sym.companionModule))
38+
39+
private case class LiftException(error: FocusError) extends Exception
40+
41+
private def constructReverseGet(
42+
companion: Term,
43+
fromType: TypeRepr,
44+
toType: TypeRepr,
45+
fromTypeArgs: List[TypeRepr]
46+
): FocusResult[Term] =
47+
// Companion.apply(value)(implicits)*
48+
(fromType.asType, toType.asType) match {
49+
case ('[f], '[t]) =>
50+
scala.util.Try('{ (to: t) =>
51+
${
52+
etaExpandIfNecessary(
53+
Select.overloaded(companion, "apply", fromTypeArgs, List('{ to }.asTerm))
54+
).fold(error => throw new LiftException(error), _.asExprOf[f])
55+
}
56+
}.asTerm) match {
57+
case scala.util.Success(term) => Right(term)
58+
case scala.util.Failure(LiftException(error)) => Left(error)
59+
case scala.util.Failure(other) => Left(FocusError.ExpansionFailed(other.toString))
60+
}
61+
}
3762
}

0 commit comments

Comments
 (0)