Is it possible to rewrite the following example using only polymorphic functions without TypeTags? The example consists of a class A[T]
which has a method matches
returning true
when applied to an instance of A
with the same type parameter T
and false
if this type parameter has a different value. Then matches
is mapped over an hlist l
of A[T]
twice resulting in an hlist of nested hlists containing results of matching each item of l
against others:
import scala.reflect.runtime.universe._
import shapeless._
class A[T: TypeTag]{
object matches extends Poly1 {
implicit def default[U: TypeTag] = at[A[U]]{ _ => typeOf[U] <:< typeOf[T] }
}
}
val l = new A[Int] :: new A[String] :: new A[Boolean] :: HNil
object matcher extends Poly1 {
implicit def default[T] = at[A[T]]{ a => l map a.matches }
}
l map matcher
Each item has one match, i.e. the result is:
(true :: false :: false :: HNil) ::
(false :: true :: false :: HNil) ::
(false :: false :: true :: HNil) :: HNil
When I try to rewrite the example without TypeTags, matches
always uses it's no
case and return false:
import shapeless._
class A[T]{
object matches extends Poly1 {
implicit def yes = at[A[T]]{_ => true}
implicit def no[U] = at[U]{_ => false}
}
}
val l = new A[Int] :: new A[String] :: new A[Boolean] :: HNil
object matcher extends Poly1 {
implicit def default[T] = at[A[T]]{ a => l map a.matches }
}
l map matcher
The result is:
(false :: false :: false :: HNil) ::
(false :: false :: false :: HNil) ::
(false :: false :: false :: HNil) :: HNil
Is it possible to rewrite this example without TypeTags and get the same result as in the first case?
It looks like you really want to be able to partially apply higher rank functions to solve this problem cleanly. This isn't possible with any kind of nice syntax out of the box, but I once wrote some code to help make it a little easier (note that this is all 1.2.4):
import shapeless._
trait ApplyMapper[HF, A, X <: HList, Out <: HList] {
def apply(a: A, x: X): Out
}
object ApplyMapper {
implicit def hnil[HF, A] = new ApplyMapper[HF, A, HNil, HNil] {
def apply(a: A, x: HNil) = HNil
}
implicit def hlist[HF, A, XH, XT <: HList, OutH, OutT <: HList](implicit
pb: Poly.Pullback2Aux[HF, A, XH, OutH],
am: ApplyMapper[HF, A, XT, OutT]
) = new ApplyMapper[HF, A, XH :: XT, OutH :: OutT] {
def apply(a: A, x: XH :: XT) = pb(a, x.head) :: am(a, x.tail)
}
}
See the answer linked above for some context.
This allows you to write the following:
class A[T]
object matches extends Poly2 {
implicit def default[T, U](implicit sub: U <:< T = null) =
at[A[T], A[U]]((_, _) => Option(sub).isDefined)
}
object mapMatcher extends Poly1 {
implicit def default[T, X <: HList, Out <: HList](
implicit am: ApplyMapper[matches.type, A[T], X, Out]
) = at[(A[T], X)] { case (a, x) => am(a, x) }
}
val l = new A[Int] :: new A[String] :: new A[Boolean] :: HNil
There may be a nicer solution, but at the moment I only have a minute to respond, and it works as is:
scala> l.zip(l mapConst l).map(mapMatcher).toList.foreach(println)
true :: false :: false :: HNil
false :: true :: false :: HNil
false :: false :: true :: HNil
As desired.