I'm manipulating shapeless records (hlists with field labels) of values of type Param[A]
. Each of them can generate an upper bound of type ParamMatcher[A]
and I've written this poly function to extract them:
object getUpperBound extends (Param ~> ParamMatcher) {
override def apply[T](param: Param[T]): ParamMatcher[T] = param.upperBound
}
When I apply it to single params it works but when I try to map over a record I get an error about missing Mapper or MapValues implicits.
This is a snippet to reproduce it (I've tried with scala 2.13.8 and shapeless 2.3.9):
import shapeless._
import shapeless.record._
import shapeless.PolyDefns.~>
import shapeless.syntax.singleton._
object MinimalRepro extends App {
trait Param[A] {
def upperBound: ParamMatcher[A]
}
sealed abstract class ParamMatcher[A]
object ParamMatcher {
case class MatchAny[A]() extends ParamMatcher[A]
}
case class UnboundedParam[A]() extends Param[A] {
override def upperBound: ParamMatcher[A] = ParamMatcher.MatchAny()
}
val stringParam = UnboundedParam[String]()
val intParam = UnboundedParam[Int]()
object getUpperBound extends (Param ~> ParamMatcher) {
override def apply[T](param: Param[T]): ParamMatcher[T] = param.upperBound
}
println(getUpperBound(stringParam)) // OK
println(getUpperBound(intParam)) // OK
println((stringParam :: intParam :: HNil).map(getUpperBound)) // KO
println((("name" ->> stringParam) :: ("retries" ->> intParam) :: HNil).mapValues(getUpperBound)) // KO
}
The reason implicit resolution fail is that UnboundedParam[Int]
is a subtype of Param[Int]
. I've solved it by writing getUpperBound
as a Poly1
that requires a proof of subtyping with <:<
:
object getUpperBound extends Poly1 {
implicit def aligned[A, P](implicit ev: P <:< Param[A]): Case.Aux[P, ParamMatcher[A]] =
at(param => ev(param).upperBound)
}