refined
refined copied to clipboard
Macro expansion error with generic Inference
I have the following generic method to convert from String
to String Refined A
.
However, this is somewhat different because I'm going to try to convert String to String Refined B
and then infer A from B, as follows:
import eu.timepit.refined.api.Inference.==>
import eu.timepit.refined.api.RefType.applyRef
import eu.timepit.refined.api.{Refined, Validate}
import eu.timepit.refined.auto._
object Inference {
def convert[B, A](value: String)(implicit I: B ==> A,
V: Validate[String,B]): Either[String, String Refined A] = {
val res = applyRef[String Refined B](value)
res.map(v => v: String Refined A)
}
}
I need a Validate[String,B]
to convert from String to String Refined B
and I need a Inference[B,A]
to convert from String Refined B
to String Refined A
. So far so good.
But this code does not compile. This is the stacktrace:
[error] java.lang.IllegalArgumentException: Could not find proxy for implicit I: eu.timepit.refined.api.Inference in List(value I, method convert, object Inference, package core, package test, package com, package <root>) (currentOwner= method wrapper )
[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)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.searchIn$1(LambdaLift.scala:315)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.proxy(LambdaLift.scala:324)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.proxyRef(LambdaLift.scala:364)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.postTransform(LambdaLift.scala:519)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:549)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:51)
[error] at scala.reflect.internal.Trees.$anonfun$itransform$2(Trees.scala:1375)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
[error] at scala.reflect.internal.Trees.itransform(Trees.scala:1373)
[error] at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
[error] at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
[error] at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
[error] at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
[error] at scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.scala$reflect$internal$Trees$UnderConstructionTransformer$$super$transform(ExplicitOuter.scala:205)
[error] at scala.reflect.internal.Trees$UnderConstructionTransformer.transform(Trees.scala:1711)
[error] at scala.reflect.internal.Trees$UnderConstructionTransformer.transform$(Trees.scala:1706)
[error] at scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.transform(ExplicitOuter.scala:282)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.preTransform(LambdaLift.scala:541)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:549)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:51)
[error] at scala.reflect.api.Trees$Transformer.$anonfun$transformStats$1(Trees.scala:2589)
[error] at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transformStats(LambdaLift.scala:567)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transformStats(LambdaLift.scala:51)
[error] at scala.reflect.internal.Trees.itransform(Trees.scala:1416)
[error] at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
[error] at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
[error] at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
[error] at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.super$transform(TypingTransformers.scala:40)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.$anonfun$transform$1(TypingTransformers.scala:40)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:25)
[error] at scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.scala$reflect$internal$Trees$UnderConstructionTransformer$$super$transform(ExplicitOuter.scala:205)
[error] at scala.reflect.internal.Trees$UnderConstructionTransformer.transform(Trees.scala:1711)
[error] at scala.reflect.internal.Trees$UnderConstructionTransformer.transform$(Trees.scala:1706)
[error] at scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.transform(ExplicitOuter.scala:282)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.preTransform(LambdaLift.scala:541)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:549)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:51)
[error] at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
[error] at scala.reflect.internal.Trees.$anonfun$itransform$4(Trees.scala:1420)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
[error] at scala.reflect.internal.Trees.itransform(Trees.scala:1419)
[error] at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
[error] at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
[error] at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
[error] at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
[error] at scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.scala$reflect$internal$Trees$UnderConstructionTransformer$$super$transform(ExplicitOuter.scala:205)
[error] at scala.reflect.internal.Trees$UnderConstructionTransformer.transform(Trees.scala:1711)
[error] at scala.reflect.internal.Trees$UnderConstructionTransformer.transform$(Trees.scala:1706)
[error] at scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.transform(ExplicitOuter.scala:282)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.preTransform(LambdaLift.scala:541)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:549)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:51)
[error] at scala.reflect.api.Trees$Transformer.$anonfun$transformStats$1(Trees.scala:2589)
[error] at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transformStats(LambdaLift.scala:567)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transformStats(LambdaLift.scala:51)
[error] at scala.reflect.internal.Trees.$anonfun$itransform$7(Trees.scala:1438)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
[error] at scala.reflect.internal.Trees.itransform(Trees.scala:1438)
[error] at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
[error] at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
[error] at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
[error] at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.super$transform(TypingTransformers.scala:40)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.$anonfun$transform$2(TypingTransformers.scala:42)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:25)
[error] at scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.scala$reflect$internal$Trees$UnderConstructionTransformer$$super$transform(ExplicitOuter.scala:205)
[error] at scala.reflect.internal.Trees$UnderConstructionTransformer.transform(Trees.scala:1711)
[error] at scala.reflect.internal.Trees$UnderConstructionTransformer.transform$(Trees.scala:1706)
[error] at scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.transform(ExplicitOuter.scala:282)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.preTransform(LambdaLift.scala:541)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:549)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:51)
[error] at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:140)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.super$transformUnit(LambdaLift.scala:573)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.$anonfun$transformUnit$1(LambdaLift.scala:573)
[error] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transformUnit(LambdaLift.scala:573)
[error] at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
[error] at scala.tools.nsc.Global$GlobalPhase.$anonfun$applyPhase$1(Global.scala:436)
[error] at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:429)
[error] at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$1(Global.scala:400)
[error] at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$1$adapted(Global.scala:400)
[error] at scala.collection.Iterator.foreach(Iterator.scala:929)
[error] at scala.collection.Iterator.foreach$(Iterator.scala:929)
[error] at scala.collection.AbstractIterator.foreach(Iterator.scala:1417)
[error] at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:400)
[error] at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1452)
[error] at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1436)
[error] at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.wrapInPackageAndCompile(ToolBoxFactory.scala:201)
[error] at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:256)
[error] at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.$anonfun$compile$13(ToolBoxFactory.scala:433)
[error] at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.apply(ToolBoxFactory.scala:359)
[error] at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:426)
[error] at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:448)
[error] at scala.reflect.macros.contexts.Evals.eval(Evals.scala:20)
[error] at scala.reflect.macros.contexts.Evals.eval$(Evals.scala:14)
[error] at scala.reflect.macros.contexts.Context.eval(Context.scala:6)
[error] at eu.timepit.refined.macros.MacroUtils.$anonfun$eval$1(MacroUtils.scala:24)
[error] at scala.Option.getOrElse(Option.scala:121)
[error] at eu.timepit.refined.macros.MacroUtils.tryN(MacroUtils.scala:28)
[error] at eu.timepit.refined.macros.MacroUtils.tryN$(MacroUtils.scala:27)
[error] at eu.timepit.refined.macros.InferMacro.tryN(InferMacro.scala:9)
[error] at eu.timepit.refined.macros.MacroUtils.eval(MacroUtils.scala:24)
[error] at eu.timepit.refined.macros.MacroUtils.eval$(MacroUtils.scala:17)
[error] at eu.timepit.refined.macros.InferMacro.eval(InferMacro.scala:9)
[error] at eu.timepit.refined.macros.InferMacro.impl(InferMacro.scala:18)
[error] res.map(v => v: String Refined A)
That's interesting! I've never worked with an explicit Inference
instance in user code. The auto.autoInfer
macro cannot work in this case since it needs to evaluate B ==> A
at compile-time to know if B
really implies A
. But there is nothing concrete it can evaluate since I
is just a parameter.
Unfortunately the instance B ==> A
is not enough to decide if B
implies A
. Here is an example:
scala> implicitly[Greater[_5] ==> Greater[_10]]
res1: Greater[_5] ==> Greater[_10] = Inference(false,greaterInferenceNat(5, 10))
Which shows that we can find an Inference
instance but this is not valid for these specific parameters.
I think what is missing to define your convert
function is a runtime equivalent for the autoInfer
macro:
def infer(tp: F[T, B])(implicit rt: RefType[F], I: B ==> A): Option[F[T, A]] =
if (I.isValid) Some(rt.unsafeRewrap[T, B, A](tp)) else None
With infer
you should be able to write convert
as:
def convert[B, A](value: String)(implicit I: B ==> A,
V: Validate[String,B]): Either[String, String Refined A] = {
val res = applyRef[String Refined B](value)
res.flatMap(v => infer(v).fold(Left("B does not implies A"))(Right.apply) )
}
Thanks. That is useful. You are right, an Inference
has a isValid
method, so I cannot assume to be always valid.
It would be cool to have a subtype of Inference which is always valid (ValidAlways
), so having an implicit evidence of it would mean you can convert it automatically (which under the hood will do this unsafeRewrap
) because it is safe.
Something like that:
sealed trait Inference {
def isValid: Boolean
def show: String
}
object Inference {
object ValidAlways extends Inference {
def isValid: Boolean = true
}
object ValidNever extends Inference {
def isValid: Boolean = false
}
final case class ValidWhen(isValid: Boolean) extends Inference
}