首页 > 解决方案 > Scalafiddle中的Simulacrum:宏扩展期间的异常

问题描述

我想在 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$3(Typers.scala:32)
    ...

这是小提琴:https ://scalafiddle.io/sf/vT0X9FR/4

我错过了什么吗?

标签: scalascala.jssimulacrum

解决方案


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

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

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

您可以看到 scastie 导致生成的代码被包装在一个Playground类中,该类未在您的代码中定义,而是由 Web IDE 为您提供的。

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

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

op如您所见, simulacrum 创建的类型没有包装。由于这种包装,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
}

推荐阅读