views:

95

answers:

4

I want to be able to write code like

10 times {
   doSomething
}

So I thought I could do that with implicits.

When i execute the following code in the Scala REPL it gets defined correctly

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

However when i try to compile,

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

it fails with the error

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

What is the difference? What am I doing wrong?

A: 

I think scala won't be able to infer return types for recursive functions for reasons that I once knew but now forget.

But in your case I can compile the file successfully except it misses one more }.

the_great_monkey
The compiler is unable the infer the resulting type of recursive functions/methods.The inference depends on the type of the resulting expression. In a recursive function the resulting expression is often the function itself, in this situation how to infer the type if it's not already known?But as @the_great_monkey pointed out, this is not the case in the question.
pedrofurla
+2  A: 

After fix the code by removing the def's {, it compiled just fine:

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

It's also recommended to remove the {} after the implicit def:

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

Also, it's worth mentioning the new { ... class content ... } is actually a structural type to compiler, so invocations to times will be made reflectively. One work-around is to create a new class:

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

Hope to have answered your question.

pedrofurla
I was initially excited, but this workaround doesn't seem to work: http://stackoverflow.com/questions/4001347/scala-compiler-says-my-method-is-recursive-in-case-when-implicits-and-anonymous-c/4002686#4002686
Alexey Romanov
Works for me. Did you `import Test._`?
Landei
The workaround is to create a non-anonymous class to contain the method.
Daniel
It compiled but was not working as expected. Now it has a named classes.
pedrofurla
A: 

Unfortunately, workaround suggested by pedrofurla doesn't appear to work (at least in 2.8.0 final):

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();
      ()
    }
  }
}
Alexey Romanov
The advice was wrong -- you need to create a class, and make the implicit just instantiate it.
Daniel
Check the answer again.
pedrofurla
+1  A: 

@tbruhn: I could not verify your problem, it compiles fine here.

I suspect that you are using some outdated version of Scala maybe?

If that's the case, the obvious fix is upgrading to Scala 2.8.x!

Otherwise, how do you compile? If you are compiling via your IDE, try to see if scalac has the same error.

soc
I was accidentally using 2.7.7. Upgrading to 2.8.0 fixed the problem.
tbruhn