【问题标题】:Scala compiler says my method is recursive in case when implicits and anonymous class is usedScala编译器说我的方法是递归的,以防使用隐式和匿名类
【发布时间】:2010-10-22 22:12:23
【问题描述】:

我希望能够写出类似的代码

10 times {
   doSomething
}

所以我想我可以用隐式来做到这一点。

当我在 Scala REPL 中执行以下代码时,它会被正确定义

scala> implicit def intToMyRichInt(count: Int) = {
    |     new {
    |       def times(f: => Unit) = {
    |         1 to count foreach { _ => f }
    |       }
    |     }
    |   }

但是当我尝试编译时,

object Test {
    implicit def intToMyRichInt(count: Int) = {
      new {
        def times(f: => Unit) = {
           1 to count foreach { _ => f }
      }
   }
}

错误提示失败

error: recursive method intToMyRichInt needs result type
1 to count foreach { _ => f }

有什么区别?我做错了什么?

【问题讨论】:

  • times 的正文后缺少的} 是复制粘贴错误?因为,除此之外,它在这里编译得很好。

标签: scala dsl anonymous-class higher-order-functions implicits


【解决方案1】:

通过删除def的{修复代码后,它编译得很好:

scala> object Test {
     |     implicit def intToMyRichInt(count: Int) = {
     |       new {
     |         def times(f: => Unit) =
     |            1 to count foreach { _ => f }
     |       }
     |    }
     | }
defined module Test

也建议去掉隐式def后​​的{}:

object Test {
    implicit def intToMyRichInt(count: Int) = 
      new {
        def times(f: => Unit) = 
           1 to count foreach { _ => f }
      }   
}

另外,值得一提的是,新的 { ... class content ... } 实际上是编译器的结构类型,因此对时间的调用将被反射性地进行。一种解决方法是创建一个新类:

object Test {
    class MyRichInt(x:Int) {
      def times(f: => Unit) =  1 to x foreach { _ => f }
    }
    implicit def intToMyRichInt(count: Int) = new MyRichInt(count)
}

希望已经回答了您的问题。

【讨论】:

  • 我一开始很兴奋,但这种解决方法似乎不起作用:stackoverflow.com/questions/4001347/…
  • 为我工作。你import Test._了吗?
  • 解决方法是创建一个非匿名类来包含该方法。
  • 它已编译但未按预期工作。现在它有一个命名类。
【解决方案2】:

@tbruhn:我无法验证您的问题,它在这里编译得很好。

我怀疑你可能在使用一些过时的 Scala 版本?

如果是这样,显而易见的解决方法是升级到 Scala 2.8.x!

要不然怎么编译?如果您是通过 IDE 编译,请尝试查看 scalac 是否有相同的错误。

【讨论】:

  • 我不小心使用了 2.7.7。升级到 2.8.0 解决了这个问题。
【解决方案3】:

我认为由于我曾经知道但现在忘记的原因,scala 将无法推断递归函数的返回类型。

但在你的情况下,我可以成功编译该文件,但它又错过了一个}。

【讨论】:

  • 编译器无法推断递归函数/方法的结果类型。推断取决于结果表达式的类型。在递归函数中,结果表达式通常是函数本身,在这种情况下,如果类型未知,如何推断类型?但正如@the_great_monkey 指出的那样,问题并非如此。
【解决方案4】:

不幸的是,pedrofurla 建议的解决方法似乎不起作用(至少在 2.8.0 最终版本中):

object Test {
    implicit def intToMyRichInt(count: Int) = 
      new ScalaObject {
        def times(f: => Unit) = 
           1 to count foreach { _ => f }
      }

    def foo = 10.times { println("foo") }
}

F:\MyProgramming\raw>scalac -Xprint:jvm Main.scala
[[syntax trees at end of jvm]]// Scala source: Main.scala
package <empty> {
  final class Test extends java.lang.Object with ScalaObject {
    final private <synthetic> <static> var reflParams$Cache1: Array[java.lang.Class] = Array[java.lang.Class]{classOf[scala.Function0]};
    @volatile private <synthetic> <static> var reflPoly$Cache1: java.lang.ref.SoftReference = new java.lang.ref.SoftReference(new scala.runtime.EmptyMethodCache());
    <synthetic> <static> def reflMethod$Method1(x$1: java.lang.Class): java.lang.reflect.Method = {
      if (Test.reflPoly$Cache1.get().$asInstanceOf[scala.runtime.MethodCache]().eq(null))
        Test.reflPoly$Cache1 = new java.lang.ref.SoftReference(new scala.runtime.EmptyMethodCache());
      var method1: java.lang.reflect.Method = Test.reflPoly$Cache1.get().$asInstanceOf[scala.runtime.MethodCache]().find(x$1);
      if (method1.ne(null))
        return method1
      else
        {
          method1 = x$1.getMethod("times", Test.reflParams$Cache1);
          Test.reflPoly$Cache1 = new java.lang.ref.SoftReference(Test.reflPoly$Cache1.get().$asInstanceOf[scala.runtime.MethodCache]().add(x$1, method1));
          return method1
        }
    };
    implicit def intToMyRichInt(count$1: Int): ScalaObject = new Test$$anon$1(count$1);
    def foo(): Unit = {
      {
        val qual1: ScalaObject = Test.this.intToMyRichInt(10);
        {
          {
            var exceptionResult1: java.lang.Object = _;
            try {
              exceptionResult1 = Test.reflMethod$Method1(qual1.getClass()).invoke(qual1, Array[java.lang.Object]{{
                (new Test$$anonfun$foo$1(): Function0)
              }})
            } catch {
              case ($1$ @ (_: java.lang.reflect.InvocationTargetException)) => {
                exceptionResult1 = throw $1$.getCause()
              }
            };
            exceptionResult1
          };
          scala.runtime.BoxedUnit.UNIT
        }
      };
      ()
    };
    def this(): object Test = {
      Test.reflParams$Cache1 = Array[java.lang.Class]{classOf[scala.Function0]};
      Test.reflPoly$Cache1 = new java.lang.ref.SoftReference(new scala.runtime.EmptyMethodCache());
      Test.super.this();
      ()
    }
  };
  @SerialVersionUID(0) @serializable final <synthetic> class Test$$anon$1$$anonfun*$1 extends scala.runtime.AbstractFunction1$mcVI$sp {
    final def apply(x$1: Int): Unit = Test$$anon$1$$anonfun*$1.this.apply$mcVI$sp(x$1);
    <specialized> def apply$mcVI$sp(v1: Int): Unit = Test$$anon$1$$anonfun*$1.this.f$1.apply$mcV$sp();
    final <bridge> def apply(v1: java.lang.Object): java.lang.Object = {
      Test$$anon$1$$anonfun*$1.this.apply(scala.Int.unbox(v1));
      scala.runtime.BoxedUnit.UNIT
    };
    <synthetic> <paramaccessor> private[this] val f$1: Function0 = _;
    def this($outer: Test$$anon$1, f$1: Function0): Test$$anon$1$$anonfun*$1 = {
      Test$$anon$1$$anonfun*$1.this.f$1 = f$1;
      Test$$anon$1$$anonfun*$1.super.this();
      ()
    }
  };
  final class Test$$anon$1 extends java.lang.Object with ScalaObject {
    def times(f$1: Function0): Unit = scala.this.Predef.intWrapper(1).to(Test$$anon$1.this.count$1).$asInstanceOf[scala.collection.immutable.Range$ByOne]().foreach$mVc$sp({
      (new Test$$anon$1$$anonfun*$1(Test$$anon$1.this, f$1): Function1)
    });
    <synthetic> <paramaccessor> private[this] val count$1: Int = _;
    def this(count$1: Int): Test$$anon$1 = {
      Test$$anon$1.this.count$1 = count$1;
      Test$$anon$1.super.this();
      ()
    }
  };
  @SerialVersionUID(0) @serializable final <synthetic> class Test$$anonfun$foo$1 extends scala.runtime.AbstractFunction0$mcV$sp {
    final def apply(): Unit = Test$$anonfun$foo$1.this.apply$mcV$sp();
    <specialized> def apply$mcV$sp(): Unit = scala.this.Predef.println("foo");
    final <bridge> def apply(): java.lang.Object = {
      Test$$anonfun$foo$1.this.apply();
      scala.runtime.BoxedUnit.UNIT
    };
    def this(): Test$$anonfun$foo$1 = {
      Test$$anonfun$foo$1.super.this();
      ()
    }
  }
}

【讨论】:

  • 建议是错误的——你需要创建一个类,并让隐式实例化它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-07
  • 1970-01-01
  • 1970-01-01
  • 2011-07-17
  • 2015-08-22
  • 2021-11-23
  • 1970-01-01
相关资源
最近更新 更多