refined icon indicating copy to clipboard operation
refined copied to clipboard

Macro expansion error with generic Inference

Open umbreak opened this issue 6 years ago • 2 comments

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)

umbreak avatar Mar 13 '18 10:03 umbreak

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

fthomas avatar Mar 13 '18 21:03 fthomas

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

}

umbreak avatar Mar 14 '18 09:03 umbreak