Разве неявный параметр в области видимости не должен быть доступен как неявный аргумент для сгенерированного макросом кода? - PullRequest
1 голос
/ 26 апреля 2019

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

Я попытался свести это к небольшому примеру, демонстрирующему поведение. Вот определение макроса:

package test

import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context

trait Read[A] {
  def read(in: String): A
}

object Read {
  def CaseClassReadImpl[A: c.WeakTypeTag](c: Context): c.Expr[Read[A]] = {
    import c.universe._
    val aType = weakTypeOf[A]

    val params = aType.decls.collect {
      case m: MethodSymbol if m.isCaseAccessor => m
    }.toList

    val paramList = params.map(param => q"Read.read[${param.typeSignature}](in)")

    val src = q"""
      new Read[$aType] {
        def read(in: String) = ${aType.typeSymbol.companion}.apply(..$paramList)
      }
    """

    c.Expr[Read[A]](src)
  }

  def readFor[A]: Read[A] = macro CaseClassReadImpl[A]

  def read[A](in: String)(implicit A: Read[A]): A = A.read(in)
}

Вот код, который его тренирует:

package test

object MacroTest {
  case class Foo[A](bar: A)

  implicit def fooRead[A](implicit A: Read[A]): Read[Foo[A]] =
    Read.readFor[Foo[A]]
}

Я ожидал, что это удастся, думая, что неявный параметр для сгенерированного вызова Read.read будет преобразован в неявный аргумент для функции fooRead. Вместо этого происходит сбой при определении fooRead с помощью:

Error:(7, 17) could not find implicit value for parameter A: test.Read[A]
    Read.readFor[Foo[A]]

Почему он не использует неявный параметр от A до fooRead? Это в лексическом контексте.

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


Обновление:

Я понял, что проблема заключается в наличии двух разных A с. Из-за вышеприведенной ошибки выглядит так, что требуемый неявный тип имеет тот же тип, что и у меня в области видимости, но после того, как я немного поковырялся с ним и явно передал неявное, мне удалось получить это (гораздо более полезное) сообщение об ошибке:

Error: type mismatch;
 found   : A(in class Foo)
 required: A(in method fooRead)
  implicit def fooRead[A](implicit read: Read[A]): Read[Foo[A]] = Read.readFor[Foo[A]]

Я до сих пор не смог найти способ заставить Scala понять, что я хочу, чтобы A s были такими же: тот, который был передан в функцию fooRead.

1 Ответ

0 голосов
/ 26 июня 2019

Попробуйте заменить

param => q"Read.read[${param.typeSignature}](in)"

с

param => q"Read.read[${param.typeSignatureIn(aType)}](in)"

https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/api/Symbols.scala#L335-L345

/** @see [[infoIn]] */
def typeSignatureIn(site: Type): Type

/** The type signature of this symbol seen as a member of given type `site`.
 *
 *  @group Basics
 */
def infoIn(site: Type): Type

/** @see [[info]] */
def typeSignature: Type

/** The type signature of this symbol.
 *
 *  This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an
 *  instantiation of a generic type. For example, signature
 *  of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`,
 *  will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature
 *  with type parameters appropriately instantiated, one should use `infoIn`.
 *
 *  @group Basics
 */
def info: Type
...