Summon Aux for higher-kinded type without referenc

2019-07-10 04:11发布

问题:

I am trying to use the Aux pattern with a higher kinded type and not have to specify the parameter of the higher-kinded type until afterward. This is similar to the SO question described here but with one significant difference, I'm going the other way around, i.e. from an implicit def back to an aux.

// The are types that I want to convert to various things
sealed trait ConversionType
trait CaseA extends ConversionType
object CaseA extends CaseA // In this case, convert to an optional
trait CaseB extends ConversionType
object CaseB extends CaseB // In this case, convert to a future etc...

trait Converter[Prefix] {
  type Paramd[_]
  def create[N](n:N): Paramd[N]
}

// Create the mechanism to convert from the cases, only doing case A for now...
object Converter {
  type Aux[Prefix, Ret[_]] = Converter[Prefix] { type Paramd[_] = Ret[_] }

  // *** Error happens here! ***
  def apply[Prefix](implicit p:Converter[Prefix]): Aux[Prefix, p.Paramd] = p

  implicit def makeOptionParamd: Aux[CaseA, Option] =
    new Converter[CaseA] {
      type Paramd[_] = Option[_]
      override def create[N](n:N): Paramd[N] = Option[N](n)
    }
}

// This seems to be fine...
val v = Converter.apply[CaseA].create("test")

I get the following compile error on the above noted line:

Error:(97, 78) type mismatch;
 found   : p.type (with underlying type Test.this.Converter[Prefix])
 required: Test.Converter.Aux[Prefix,p.Paramd]
    (which expands to)  Test.this.Converter[Prefix]{type Paramd[_] = p.Paramd[_]}
    def apply[Prefix](implicit p:Converter[Prefix]): Aux[Prefix, p.Paramd] = p

What am I doing wrong?

回答1:

What you probably want is

object Converter {
  type Aux[Prefix, Ret[_]] = Converter[Prefix] { type Paramd[A] = Ret[A] }

  // compiles
  def apply[Prefix](implicit p:Converter[Prefix]): Aux[Prefix, p.Paramd] = p

  implicit def makeOptionParamd: Aux[CaseA, Option] =
    new Converter[CaseA] {
      type Paramd[A] = Option[A]
      override def create[N](n:N): Paramd[N] = Option[N](n)
    }
}

When you write

type Paramd[_] = Ret[_]

the _ in the left and the right parts are unrelated. It's the same as

type Paramd[A] = Ret[_]

type Paramd[A] = Ret[B] forSome { type B }

So Aux[Prefix, p.Paramd] with your definition is equivalent to Converter[Prefix] { type Paramd[A] = p.Paramd[_] }, and p doesn't have this type because p.Paramd[A] is not p.Paramd[_].