Scala - бесформенный неявный параметр Generic.Aux, не найденный в unapply
Я столкнулся со следующей проблемой с импликациями в Scala, используя Shapeless's Generic.Aux
:
case class Complex(re: Double, im: Double)
object Prod2 {
def unapply[C, A, B](c: C)(implicit C: Generic.Aux[C, A :: B :: HNil]) = Some((C.to(c).head, C.to(c).tail.head))
}
val c = Complex(1.0, 2.0)
val Prod2(re, im) = c
Код выше не компилируется. Сообщает
Error:(22, 7) could not find implicit value for parameter C: shapeless.Generic.Aux[nexus.ops.Test.Complex,A :: B :: shapeless.HNil]
val Prod2(re, im) = c
Error:(22, 7) not enough arguments for method unapply: (implicit C: shapeless.Generic.Aux[nexus.ops.Test.Complex,A :: B :: shapeless.HNil])Some[(A, B)].
Unspecified value parameter C.
val Prod2(re, im) = c
Тем не менее, если я вручную
implicitly[Generic.Aux[Complex, Double :: Double :: HNil]]
совершенно нормально получить этот неявный экземпляр.
2 ответа
Следующий код работает:
import shapeless.ops.hlist.IsHCons
import shapeless.{::, Generic, HList, HNil}
case class Complex(re: Double, im: Double)
object Prod2 {
def unapply[C, L <: HList, H, T <: HList, H1, T1 <: HList](c: C)(implicit
C: Generic.Aux[C, L],
isHCons: IsHCons.Aux[L, H, T],
isHCons1: IsHCons.Aux[T, H1, T1]) = Some((C.to(c).head, C.to(c).tail.head))
}
val c = Complex(1.0, 2.0)
val Prod2(re, im) = c
К сожалению, компилятор просто не достаточно умен, чтобы выполнить объединение, которое было бы необходимо для вывода A
а также B
Вот. Вы можете прочитать о некоторых деталях этой проблемы в разделе 4.3 Руководства Underscore Type Astronaut по Shapeless. Книга предлагает обходной путь, используя IsHCons
, но в этом случае я думаю, что требуется <:<
доказательство немного чище:
import shapeless.{::, Generic, HList, HNil}
case class Complex(re: Double, im: Double)
object Prod2 {
def unapply[C, L <: HList, A, B](c: C)(implicit
C: Generic.Aux[C, L],
ev: L <:< (A :: B :: HNil)
) = Some((C.to(c).head, C.to(c).tail.head))
}
А потом:
scala> val c = Complex(1.0, 2.0)
c: Complex = Complex(1.0,2.0)
scala> val Prod2(re, im) = c
re: Double = 1.0
im: Double = 2.0
Это разочаровывает, но это обходной путь, который вам понадобится снова и снова, если вы работаете с Shapeless, поэтому хорошо иметь его в своем наборе инструментов.