2010-10-22 34 views
3

Je veux être en mesure d'écrire du code commecompilateur Scala dit ma méthode est récursive dans le cas où implicits et classe anonyme est utilisé

10 times { 
    doSomething 
} 

Je pensais que je pouvais le faire avec implicits.

Quand j'exécute le code suivant dans la Scala REPL il se définit correctement

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

Cependant quand je tente de compiler,

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

il échoue avec l'erreur

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

Quelle est la différence? Qu'est-ce que je fais mal?

+0

Le '}' manquant après le corps de 'times' est une erreur de copier-coller? Parce que, à part ça, ça compile bien ici. –

Répondre

0

Je pense que scala ne sera pas en mesure d'inférer les types de retour pour les fonctions récursives pour des raisons que je connaissais une fois mais que j'oublie maintenant.

Mais dans votre cas, je peux compiler le fichier avec succès, sauf qu'il en manque un de plus}.

+0

Le compilateur est incapable de déduire le type résultant de fonctions/méthodes récursives. L'inférence dépend du type de l'expression résultante. Dans une fonction récursive, l'expression résultante est souvent la fonction elle-même, dans cette situation comment déduire le type si ce n'est pas déjà connu? Mais comme @the_great_monkey l'a fait remarquer, ce n'est pas le cas dans la question. – pedrofurla

2

Après fixer le code en enlevant de la def {, il a compilé très bien:

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

Il est également recommandé d'enlever le {} après la définition implicite:

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

En outre, il vaut la peine mentionner le nouveau {... class content ...} est en fait un type structurel à compiler, donc les invocations aux temps seront faites de façon réfléchie. Une solution consiste à créer une nouvelle classe:

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

Nous espérons avoir répondu à votre question.

+0

J'étais initialement excité, mais cette solution de contournement ne semble pas fonctionner: http: // stackoverflow.com/questions/4001347/scala-compilateur-dit-ma-méthode-est-récursif-dans-cas-quand-implicite-et-anonyme-c/4002686 # 4002686 –

+0

Fonctionne pour moi. Avez-vous 'importé Test._'? – Landei

+1

La solution de contournement consiste à créer une classe non anonyme pour contenir la méthode. –

0

Malheureusement, solution proposée par pedrofurla ne semble pas fonctionner (au moins en 2.8.0 finale):

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(); 
    () 
    } 
    } 
} 
+0

Le conseil était faux - vous devez créer une classe, et faire que l'implicite l'instancie juste. –

+0

Vérifiez à nouveau la réponse. – pedrofurla

1

@tbruhn: Je ne pouvais pas vérifier votre problème, il compile bien ici.

Je suppose que vous utilisez peut-être une version obsolète de Scala?

Si c'est le cas, le correctif évident est la mise à niveau vers Scala 2.8.x!

Sinon, comment compilez-vous? Si vous compilez via votre IDE, essayez de voir si scalac a la même erreur.

+0

J'utilisais accidentellement 2.7.7. La mise à niveau vers la version 2.8.0 a résolu le problème. – tbruhn