Scalafiddle 中的 Simulacrum:宏扩展期间的异常

Simulacrum in Scalafiddle: exception during macro expansion

我想在 Scalafiddle 中使用 Simulacrum,例如:

import simulacrum._

@typeclass trait Ordering[T] {
    def compare(x: T, y: T): Int
    @op("<") def lt(x: T, y: T): Boolean = compare(x, y) < 0
    @op(">") def gt(x: T, y: T): Boolean = compare(x, y) > 0
}

这给了我以下错误:

ScalaFiddle.scala:3: error: exception during macro expansion: 
scala.reflect.macros.TypecheckException: not found: type op
    at scala.reflect.macros.contexts.Typers.$anonfun$typecheck(Typers.scala:32)
    ...

这是fiddle:https://scalafiddle.io/sf/vT0X9FR/4

我是不是漏掉了什么?

你的代码没有问题,问题出在ScalaFiddle。

如果我尝试 运行 你在 scastie 中的代码(类似于 Scala 的 web IDE),并打印出它生成的类型树,你可以看到以下内容:

|-- class Playground BYVALmode-EXPRmode (site: package <empty>) 
|    |-- new op("<") EXPRmode (silent: class Playground) 

您可以看到 scastie 导致生成的代码被包裹在 Playground class 中,这不是您的代码中定义的,而是网络为您提供的 IDE.

如果我在 IDEA 中编译相同的示例,我会看到以下内容:

|-- new op("<") EXPRmode (silent: package github) 
|    |-- new op BYVALmode-EXPRmode-FUNmode-POLYmode (silent: package github) 

如您所见,没有由 simulacrum 创建的 op 类型的换行。由于这种包装,Simulacrum 无法找到它生成的 op 类型,因为它在编译时的完整命名空间是 Playground.op.

为避免这种情况并作为解决方法,将您的特征包装在一个对象中:

import simulacrum._

object Foo {
  @typeclass trait Ordering[T] {
    def compare(x: T, y: T): Int
    @op("<") def lt(x: T, y: T): Boolean = compare(x, y) < 0
    @op(">") def gt(x: T, y: T): Boolean = compare(x, y) > 0
  }

  @typeclass trait Numeric[T] extends Ordering[T] {
    @op("+") def plus(x: T, y: T): T
    @op("*") def times(x: T, y: T): T
    @op("unary_-") def negate(x: T): T
    def zero: T
    def abs(x: T): T = if (lt(x, zero)) negate(x) else x
  }

  import Foo.Numeric.ops._
  def signOfTheTimes[T: Numeric](t: T): T = -(t.abs) * t
}