Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Macro expansion error with generic Inference #454

Open
umbreak opened this issue Mar 13, 2018 · 2 comments · May be fixed by #464
Open

Macro expansion error with generic Inference #454

umbreak opened this issue Mar 13, 2018 · 2 comments · May be fixed by #464

Comments

@umbreak
Copy link
Contributor

umbreak commented Mar 13, 2018

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)

@fthomas
Copy link
Owner

fthomas commented Mar 13, 2018

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

@umbreak
Copy link
Contributor Author

umbreak commented Mar 14, 2018

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 added a commit to umbreak/refined that referenced this issue Mar 29, 2018
Fixes fthomas#454

Here I've tried to have Inference as a trait and added 2 subtypes
- InferAlways
- InferWhen

With that we can directly have a safe conversion whenever an InferAlways is presen
@umbreak umbreak linked a pull request Mar 29, 2018 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants