如何在Scala中定位隐式的来源?

5

简短问题:

有没有一种方法可以要求Scala编译器告诉我在程序中的某个点使用的某个隐式是在哪里声明的?

如果没有,是否有算法可以手动跟随以找出隐式声明的位置?

详细问题:

我正在遵循一个简单的spray crud 教程

在下面的代码片段中(来自教程的repo):

pathEnd {
  post {
    entity(as[Question]) { question =>
      completeWithLocationHeader(
        resourceId = questionService.createQuestion(question),
        ifDefinedStatus = 201, ifEmptyStatus = 409)
      }
    }
} ~

as 接受类型为 FromRequestUnmarshaller[T] 的隐式参数 (完整源代码请参见此处):

  def as[T](implicit um: FromRequestUnmarshaller[T]) = um

当我在IntelliJ中使用CMD+SHIFT+P时,询问这个implicit是从哪里来的时,我得到以下回答: enter image description here 然后,当我遵循第一个提示去查看(链接如下),我会得到以下结果: hint
trait UnmarshallerLifting {

  implicit def fromRequestUnmarshaller[T](implicit um: FromMessageUnmarshaller[T]): FromRequestUnmarshaller[T] =
    new FromRequestUnmarshaller[T] {
      def apply(request: HttpRequest): Deserialized[T] = um(request)
    }
...

这并没有帮助我找出隐式的 FromRequestUnmarshaller[T] 是从哪里来的,因为我无法确定 trait UnmarshallerLifting 如何混入到 QuestionResource 中,如果我检查类层次结构:

enter image description here

我检查了看起来可能包含此隐式的 traits,例如这个 trait,但它并不包含该隐式:

trait MarshallingDirectives {
  import BasicDirectives._
  import MiscDirectives._
  import RouteDirectives._

  /**
   * Unmarshalls the requests entity to the given type passes it to its inner Route.
   * If there is a problem with unmarshalling the request is rejected with the [[spray.routing.Rejection]]
   * produced by the unmarshaller.
   */
  def entity[T](um: FromRequestUnmarshaller[T]): Directive1[T] =
    extract(_.request.as(um)).flatMap[T :: HNil] {
      case Right(value)                            ⇒ provide(value)
      case Left(ContentExpected)                   ⇒ reject(RequestEntityExpectedRejection)
      case Left(UnsupportedContentType(supported)) ⇒ reject(UnsupportedRequestContentTypeRejection(supported))
      case Left(MalformedContent(errorMsg, cause)) ⇒ reject(MalformedRequestContentRejection(errorMsg, cause))
    } & cancelAllRejections(ofTypes(RequestEntityExpectedRejection.getClass, classOf[UnsupportedRequestContentTypeRejection]))

  /**
   * Returns the in-scope FromRequestUnmarshaller for the given type.
   */
  def as[T](implicit um: FromRequestUnmarshaller[T]) = um

  /**
   * Uses the marshaller for the given type to produce a completion function that is passed to its inner route.
   * You can use it do decouple marshaller resolution from request completion.
   */
  def produce[T](marshaller: ToResponseMarshaller[T]): Directive[(TUnit) :: HNil] =
    extract { ctx ⇒ (value: T) ⇒ ctx.complete(value)(marshaller) } & cancelAllRejections(ofType[UnacceptedResponseContentTypeRejection])

  /**
   * Returns the in-scope Marshaller for the given type.
   */
  def instanceOf[T](implicit m: ToResponseMarshaller[T]) = m

  /**
   * Completes the request using the given function. The input to the function is produced with the in-scope
   * entity unmarshaller and the result value of the function is marshalled with the in-scope marshaller.
   */
  def handleWith[A, B](f: AB)(implicit um: FromRequestUnmarshaller[A], m: ToResponseMarshaller[B]): Route =
    entity(um) { a ⇒ RouteDirectives.complete(f(a)) }
}

object MarshallingDirectives extends MarshallingDirectives

我已经查看了20个不同的地方,但是感到沮丧。

有没有一种方法可以要求scala编译器告诉我在程序中的某个点(例如这里)使用了一个特定的隐式(在这个例子中是FromRequestUnmarshaller[T])是在哪里被声明的?

如果没有这样的方法,是否有一种算法可以手动遵循以找出隐式类型的声明位置?

我在Google和SOF上寻找了这个问题,但是找到的提示都没有帮助。我也阅读了这篇文章,但仍然不知道FromRequestUnmarshaller[T]来自哪里。


1
也许您正在寻找:https://dev59.com/6ZPfa4cB1Zd3GeqPATcS#34903876? - Michael Zajac
好的,谢谢,我会看一下的。 - jhegedus
3个回答

4
通常我会在编译器中启用-Xlog-implicits以查看隐式操作的情况。
此外,spray已经被弃用,建议使用akka-http。我建议进行切换。

我尝试过了,但它并没有告诉我隐式参数是从哪里来的,它只是抱怨那些被尝试但未能工作的隐式参数 :( - jhegedus

4
我按照Michael的建议进行了以下操作:

(在HTML标签内保留原有内容)

    import scala.reflect.runtime.universe.reify
    println(reify(entity(as[Question])))

它打印出了:
Expr[spray.routing.Directive1[spray_examples.plain_rest.danielasfregola.quiz.management.entities.Question]](QuestionResource.entity(QuestionResource.as[Question](Deserializer.fromRequestUnmarshaller(Deserializer.fromMessageUnmarshaller(QuestionResource.json4sUnmarshaller(ManifestFactory.classType(classOf[spray_examples.plain_rest.danielasfregola.quiz.management.entities.Question])))))))

这直接告诉我们隐式是从哪里来的:Deserializer.fromRequestUnmarshaller

此外,还有另一种方法,使用InteliJ的搜索用法功能:

enter image description here


0

Odersky在他的书中推荐的调试隐式参数的方法是使用scalac-Xprint:typer选项,你可以在这里详细了解。

以下是复制的内容:

    object Mocha extends Application {
  
      class PreferredDrink(val preference: String)
  
      implicit val pref = new PreferredDrink("mocha")
  
      def enjoy(name: String)(implicit drink: PreferredDrink) {
        print("Welcome, "+ name)
        print(". Enjoy a ")
        print(drink.preference)
        println("!")
      }
  
      enjoy("reader")
    }

  $ scalac -Xprint:typer mocha.scala
  [[syntax trees at end of typer]]
// Scala source: mocha.scala
  package <empty> {
    final object Mocha extends java.lang.Object with Application
        with ScalaObject {
  
      
// ...
  
      private[this] val pref: Mocha.PreferredDrink =
        new Mocha.this.PreferredDrink("mocha");
      implicit <stable> <accessor>
        def pref: Mocha.PreferredDrink = Mocha.this.pref;
      def enjoy(name: String)
          (implicit drink: Mocha.PreferredDrink): Unit = {
        scala.this.Predef.print("Welcome, ".+(name));
        scala.this.Predef.print(". Enjoy a ");
        scala.this.Predef.print(drink.preference);
        scala.this.Predef.println("!")
      };
      Mocha.this.enjoy("reader")(Mocha.this.pref)
    }
  }

再次感谢Odersky:https://www.artima.com/pins1ed/implicit-conversions-and-parameters.html#21.7


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接