Невозможно получить неявный экземпляр класса типа, используя бесформенный - PullRequest
1 голос
/ 29 апреля 2020

Я хочу получить экземпляры классов типов из унарных классов. Но когда я пытаюсь неявно получить его, я всегда получаю сообщение об ошибке. Если я получаю это явно, используя неявный метод - это работает. Я не уверен, но, возможно, причина в том, что я пропустил некоторые неявные типы в своей функции

import shapeless._

import scala.reflect.ClassTag
import scala.reflect.runtime.universe.TypeTag

sealed trait Foo[A] {
  def hello(): Unit
}

object Foo {
  def apply[A](implicit foo: Foo[A]): foo.type = foo

  def instance[A](implicit tag: ClassTag[A]): Foo[A] = new Foo[A] {
    override def hello(): Unit = println(s"Hello from ${tag.runtimeClass.getName}")
  }
}

trait Instances extends LowestPriority {
  implicit val intHelloInstance: Foo[Int] = Foo.instance[Int]
}

trait LowestPriority {
  implicit def derive[A: TypeTag, L <: HList, H](
    implicit gen: Generic.Aux[A, L],
    H: Lazy[Foo[H]],
    isUnary: (H :: HNil) =:= L
  ): Foo[A] =
    new Foo[A] {
      override def hello(): Unit = {
        print(s"Derived: ")
        H.value.hello()
      }
    }
}

object T extends Instances {

  case class A(a: Int)

  def main(args: Array[String]): Unit = {
    intHelloInstance.hello()
//    val a: Foo[A] = derive[A, Int :: HNil, Int] // this works
//    a.hello()
    Foo[A].hello() // error
  }
}

Из журналов:

Информация: (45, 8) бесформенный. this.Generi c .materialize не является допустимым неявным значением для shapeless.Generi c .Aux [H, L], поскольку: hasMatchingSymbol сообщил об ошибке: H не является классом случая, подобным классу случая, запечатанной чертой или Unit Foo [A] .hello ()

Как я могу это исправить?

1 Ответ

1 голос
/ 29 апреля 2020

Это один из случаев, когда поведение зависит от ... порядка устранения последствий.

Если вы измените сигнатуру на:

  implicit def derive[A, L <: HList, H](
    implicit gen: Generic.Aux[A, L],
    isUnary: (H :: HNil) =:= L, // swapped
    H: Lazy[Foo[H]]             // with this
  ): Foo[A] = ...

, компилятор:

  • попытайтесь найти HList L, который мог бы быть в паре с A
  • , затем докажите, что L равен некоторому H :: HNil, вычисляющему H в процессе
  • наконец, используя H для извлечения Lazy[Foo[H]]

, и вы успешно скомпилируете Foo[A].hello().

Когда эти два последних аргумента поменялись местами с тем, что у вас есть в вашем вопросе, компилятор должен

  • принять некоторое H (что в нашем тестовом примере, скорее всего, НЕ БУДЕТ Int)
  • , затем принудительно попытайтесь настроить L для соответствия
  • , затем go обратно на Generic, который теперь вынужден доказать, что A представимо некоторыми H :: HNil где H, скорее всего, не Int и не в состоянии это сделать, но с ошибочной информацией об ошибке

Это один из этих случаев, который показывает, что бесформенный деривация на основе иногда бывает сложной, а также здесь показано, что бесформенные авторы предполагали в макросах, что наиболее вероятной причиной сбоя раскрытия макроса является то, что A не относится к классу падежей, в то время как мы только что увидели, что это может быть также компилятор, приводящий к некоторым невозможным доказательствам, потому что логический вывод неверен, потому что мы решил их в неправильном порядке.

...