refined icon indicating copy to clipboard operation
refined copied to clipboard

`Refined AnyOf[T :: HNil]` is not compatible with `Refined T`

Open vasily-kirichenko opened this issue 7 years ago • 7 comments

image

vasily-kirichenko avatar Apr 29 '18 20:04 vasily-kirichenko

This requires an Inference instances that proofs that P implies AnyOf[L <: HList] if L contains P. It would probably have a signature like this:

implicit def partOfAnyOf[P, L <: HList](implicit shapeless.Selector[L, P]): P ==> AnyOf[L] = ???

fthomas avatar Apr 30 '18 05:04 fthomas

I'm new to Shapeless, I'm stuck with the following:

implicit def partOfAnyOf[P, L <: HList](implicit sel: Selector[L, P]): P ==> AnyOf[L] =
  Inference(sel(?), "?")

vasily-kirichenko avatar May 01 '18 08:05 vasily-kirichenko

You don't need to do anything with the sel parameter because if the compiler finds an instance Selector[L, P] it already has proven that L contains P.

So

implicit def partOfAnyOf[P, L <: HList](implicit sel: Selector[L, P]): P ==> AnyOf[L] =
  Inference(true, "partOfAnyOf")

should be enough to make your initial code compile.

fthomas avatar May 01 '18 13:05 fthomas

Thanks. Now something goes really wrong:

import eu.timepit.refined.api.Inference.==>
import eu.timepit.refined.api._
import eu.timepit.refined.auto._
import eu.timepit.refined.boolean.AnyOf
import eu.timepit.refined.char._
import shapeless._
import shapeless.ops.hlist.Selector

object Main extends App {
  implicit def partOfAnyOf[P, L <: HList](implicit sel: Selector[L, P]): P ==> AnyOf[L] =
    Inference(true, "partOfAnyOf")

  val digit: Char Refined Digit = '1'

  val code: Char Refined AnyOf[Digit :: Letter :: Whitespace :: HNil] = digit
Error:(15, 73) exception during macro expansion: 
java.lang.ClassNotFoundException: Main$
	at scala.reflect.internal.util.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:64)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at __wrapper$3$8c7b3e7581aa4551ab51a6293a2a3bd5.__wrapper$3$8c7b3e7581aa4551ab51a6293a2a3bd5$.wrapper(<no source file>:15)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.$anonfun$compile$11(ToolBoxFactory.scala:279)
	at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:448)
	at scala.reflect.macros.contexts.Evals.eval(Evals.scala:20)
	at scala.reflect.macros.contexts.Evals.eval$(Evals.scala:14)
	at scala.reflect.macros.contexts.Context.eval(Context.scala:6)
	at eu.timepit.refined.macros.MacroUtils.$anonfun$eval$1(MacroUtils.scala:24)
	at scala.Option.getOrElse(Option.scala:121)
	at eu.timepit.refined.macros.MacroUtils.tryN(MacroUtils.scala:28)
	at eu.timepit.refined.macros.MacroUtils.tryN$(MacroUtils.scala:27)
	at eu.timepit.refined.macros.InferMacro.tryN(InferMacro.scala:9)
	at eu.timepit.refined.macros.MacroUtils.eval(MacroUtils.scala:24)
	at eu.timepit.refined.macros.MacroUtils.eval$(MacroUtils.scala:17)
	at eu.timepit.refined.macros.InferMacro.eval(InferMacro.scala:9)
	at eu.timepit.refined.macros.InferMacro.impl(InferMacro.scala:18)
  val code: Char Refined AnyOf[Digit :: Letter :: Whitespace :: HNil] = digit

Error:(15, 73) type mismatch;
 found   : eu.timepit.refined.api.Refined[Char,eu.timepit.refined.char.Digit]
 required: eu.timepit.refined.api.Refined[Char,eu.timepit.refined.boolean.AnyOf[eu.timepit.refined.char.Digit :: eu.timepit.refined.char.Letter :: eu.timepit.refined.char.Whitespace :: shapeless.HNil]]
  val code: Char Refined AnyOf[Digit :: Letter :: Whitespace :: HNil] = digit

vasily-kirichenko avatar May 01 '18 15:05 vasily-kirichenko

The good news is that your Inference instance now works as intended (otherwise you wouldn't see that stack trace) but the bad news is that you're hitting the problem described in https://github.com/fthomas/refined/blob/master/modules/docs/macro_pitfalls.md. For testing the instance it should be enough if you put the instance in your main sources and the test code in the test sources.

fthomas avatar May 01 '18 19:05 fthomas

This does not help:

object Implicits {
  implicit def partOfAnyOf[P, L <: HList](implicit sel: Selector[L, P]): P ==> AnyOf[L] =
    Inference(isValid = true, "partOfAnyOf")
}

object Main extends App {
  import Implicits._

  type ProductCode = Char Refined AnyOf[Digit :: Letter :: Whitespace :: HNil]

  val digit: Char Refined Digit = '1'

  val code: ProductCode = digit
}

Maybe it's a good idea to add this implicit right into Refined itself? I think it's completely general.

vasily-kirichenko avatar May 02 '18 08:05 vasily-kirichenko

I agree, it would be a welcome addition.

fthomas avatar May 02 '18 13:05 fthomas