Implicit inference for ops.record.Selector doesn't work when Id is passed to a HKT
tksfz opened this issue · comments
Welcome to Scala 2.12.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_172).
Type in expressions for evaluation. Or try :help.
scala> :settings
-Xprint = List()
-Ybrowse = List()
-Ypartial-unification = true
-Yrepl-sync = true
-Ytyper-debug = false
-d = .
-nowarn = false
scala> case class Hkt[F[_]](foo: F[String])
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
defined class Hkt
scala> import shapeless._
import shapeless._
scala> def func[T] = new { def apply[R <: HList](k: Witness)(implicit gen: LabelledGeneric.Aux[T, R], selector: ops.record.Selector[R, k.T]) = 0 }
func: [T]=> AnyRef{def apply[R <: shapeless.HList](k: shapeless.Witness)(implicit gen: shapeless.LabelledGeneric.Aux[T,R],implicit selector: shapeless.ops.record.Selector[R,k.T]): Int}
scala> func[Hkt[Option]].apply('foo)
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
res1: Int = 0
scala> func[Hkt[Id]].apply('foo)
<console>:18: error: could not find implicit value for parameter gen: shapeless.LabelledGeneric[Hkt[[+T]T]]{type Repr = R}
func[Hkt[Id]].apply('foo)
^
Note that func[Hkt[Id]].apply('foo)
can be made to work if I do a (somewhat verbose) work-around where I push implicit gen: LabelledGeneric
up to the partially applied class.
For all I know, this could be a bug in the scala compiler.
With -Xlog-implicits
(on Scala 2.13.0-RC1):
[info] /home/georgi/work/oss/scratch/src/main/scala/HktId.scala:7:22: shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[HktId.Hkt[[+T]T],V] because:
[info] hasMatchingSymbol reported error: type mismatch;
[info] found : HktId.Hkt[Any]
[info] required: HktId.Hkt[[+T]T]
[info] Note: Any >: [+T]T, but class Hkt is invariant in type F.
[info] You may wish to define F as -F instead. (SLS 4.5)
[info] func[Hkt[Id]].apply('foo)
[info] ^
[info] /home/georgi/work/oss/scratch/src/main/scala/HktId.scala:7:22: shapeless.this.LabelledGeneric.materializeCoproduct is not a valid implicit value for shapeless.LabelledGeneric[HktId.Hkt[[+T]T]]{type Repr = R} because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter gen: shapeless.Generic.Aux[HktId.Hkt[[+T]T],V]
[info] func[Hkt[Id]].apply('foo)
[info] ^
[info] /home/georgi/work/oss/scratch/src/main/scala/HktId.scala:7:22: shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[HktId.Hkt[[+T]T],V] because:
[info] hasMatchingSymbol reported error: type mismatch;
[info] found : HktId.Hkt[Any]
[info] required: HktId.Hkt[[+T]T]
[info] Note: Any >: [+T]T, but class Hkt is invariant in type F.
[info] You may wish to define F as -F instead. (SLS 4.5)
[info] func[Hkt[Id]].apply('foo)
[info] ^
[info] /home/georgi/work/oss/scratch/src/main/scala/HktId.scala:7:22: shapeless.this.LabelledGeneric.materializeProduct is not a valid implicit value for shapeless.LabelledGeneric[HktId.Hkt[[+T]T]]{type Repr = R} because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter gen: shapeless.Generic.Aux[HktId.Hkt[[+T]T],V]
[info] func[Hkt[Id]].apply('foo)
[info] ^
[error] /home/georgi/work/oss/scratch/src/main/scala/HktId.scala:7:22: could not find implicit value for parameter gen: shapeless.LabelledGeneric[HktId.Hkt[[+T]T]]{type Repr = R}
[error] func[Hkt[Id]].apply('foo)
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
That Hkt[Any]
is suspicious.
Ah, ofc, it's because this doesn't work either:
val x: Hkt[Id] = Hkt("foo")
[error] found : HktId.Hkt[Any]
[error] required: HktId.Hkt[shapeless.Id]
[error] Note: Any >: shapeless.Id, but class Hkt is invariant in type F.
[error] You may wish to define F as -F instead. (SLS 4.5)
[error] val x: Hkt[Id] = Hkt("foo")
Code like this is generated by the Generic
macro.
Does the contravariant workaround -F
actually work? It didn't seem to in my quick attempt.
I wouldn't expect it to work. In this case the error message is not helpful. This is one of the issues with Id
- unification failed for Hkt("foo")
because it expects a type with shape F[String]
but we gave it a naked String
. It doesn't know that we actually meant Hkt[Id]("foo")
. It looks a bit silly in this case but it can't be fixed in general I think and it's more of a scalac
than shapeless
issue.
Hmm, actually it works in dotty:
scala> case class Hkt[F[_]](foo: F[String])
// defined case class Hkt
scala> type Id[+T] = T
// defined alias type Id = [+T] => T
scala> val x: Hkt[Id] = Hkt("foo")
val x: Hkt[Id] = Hkt(foo)
So it can be fixed!