Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructing TypeTags of higher-kinded types

Given a simple parametrized type like class LK[A], I can write

// or simpler def tagLK[A: TypeTag] = typeTag[LK[A]]
def tagLK[A](implicit tA: TypeTag[A]) = typeTag[LK[A]]

tagLK[Int] == typeTag[LK[Int]] // true

Now I'd like to write an analogue for class HK[F[_], A]:

def tagHK[F[_], A](implicit ???) = typeTag[HK[F, A]] 
// or some other implementation?

tagHK[Option, Int] == typeTag[HK[Option, Int]]

Is this possible? I've tried

def tagHK[F[_], A](implicit tF: TypeTag[F[_]], tA: TypeTag[A]) = typeTag[HK[F, A]]

def tagHK[F[_], A](implicit tF: TypeTag[F], tA: TypeTag[A]) = typeTag[HK[F, A]]

but neither works for the obvious reasons (in the first case F[_] is the existential type instead of the higher-kinded one, in the second TypeTag[F] doesn't compile).

I suspect the answer is "it's impossible", but would be very happy if it isn't.

EDIT: we currently use WeakTypeTags as follows (slightly simplified):

trait Element[A] {
  val tag: WeakTypeTag[A]
  // other irrelevant methods
}

// e.g.
def seqElement[A: Element]: Element[Seq[A]] = new Element[Seq[A]] {
  val tag = {
    implicit val tA = implicitly[Element[A]].tag
    weakTypeTag[Seq[A]]
  }
}

trait Container[F[_]] {
  def lift[A: Element]: Element[F[A]]

  // note that the bound is always satisfied, but we pass the 
  // tag explicitly when this is used
  def tag[A: WeakTypeTag]: WeakTypeTag[F[A]]
}

val seqContainer: Container[Seq] = new Container[Seq] {
  def lift[A: Element] = seqElement[A]
}

All of this works fine if we replace WeakTypeTag with TypeTag. Unfortunately, this doesn't:

class Free[F[_]: Container, A: Element]

def freeElement[F[_]: Container, A: Element] {
  val tag = {
    implicit val tA = implicitly[Element[A]].tag
    // we need to get something like TypeTag[F] here
    // which could be obtained from the implicit Container[F]
    typeTag[Free[F, A]]
  }
}
like image 905
Alexey Romanov Avatar asked Apr 25 '15 10:04

Alexey Romanov


1 Answers

Does this serve your purposes?

def tagHK[F[_], A](implicit tt: TypeTag[HK[F, A]]) = tt

As opposed to using the implicit parameter to get the TypeTag for F and A separately and then composing them, you can directly request the tag you want from the compiler. This passes your test case as desired:

tagHK[Option, Int] == typeTag[HK[Option, Int]] //true

Alternatively, if you have an instance of TypeTag[A] you could try:

object HK {
    def apply[F[_]] = new HKTypeProvider[F]
    class HKTypeProvider[F[_]] {
        def get[A](tt: TypeTag[A])(implicit hktt: TypeTag[HK[F, A]]) = hktt
    }
}

Allowing you to do:

val myTT = typeTag[Int]
HK[Option].get(myTT) == typeTag[HK[Option, Int]] //true
like image 109
Ben Reich Avatar answered Sep 23 '22 09:09

Ben Reich