2010-10-04 14 views
2
open System 

type Foo() = 
    interface Collections.IEnumerable with 
     member x.GetEnumerator() = null 

type Bar() = 
    interface Collections.IEnumerable with 
     member x.GetEnumerator() = null 
    interface Collections.Generic.IEnumerable<int> with 
     member x.GetEnumerator() = null 

let xs, ys = Foo(), Bar() 

for x in xs do() // <-- 
for y in ys do() // fine 

Le code produit ci-dessus l'erreur de compilation suivante:Est-ce un bug du compilateur F #? # 2

The type 'Foo' is not a type whose values can be enumerated with this syntax, i.e. is not compatible with either seq<_>, IEnumerable<_> or IEnumerable and does not have a GetEnumerator method.

Le code semble parfaitement légal et la version générique fonctionne très bien. Est-ce un bug du compilateur F #?

Répondre

5

Je pense que c'est un décalage entre le message d'erreur d'un à la spécification. Comme KVB souligne, la spécification permet for ... in que dans deux cas:

  • Lorsque le type implémente l'interface IEnumerable<_> générique (aka seq<_>)
  • Lorsque le type a une méthode GetEnumerator qui retourne le type avec certaines propriétés

Si le type implémente une interfaceIEnumerable non générique, alors il ne correspond à aucune des deux conditions. Toutefois, si vous le transtypez à IEnumerable, il s'agira en fait du type IEnumerable, qui correspond à la deuxième condition. Avoir un membre GetEnumerator directement dans le type (comme le suggère le desco) est également correct, car il correspond également au second cas. Donc, je pense que le message d'erreur est incorrect, car il dit que l'implémentation non générique IEnumerable est suffisante, mais ce n'est pas le cas.

Cependant, il semble y avoir une erreur de compilation réelle liée à la boucle for. Vous obtenez un compilateur « erreur interne » lorsque vous écrivez le code suivant (ce qui est inexact, car le type de retour générique inférées ne met pas en œuvre IEnumerator):

type Foo() = 
    member x.GetEnumerator() = null 
for x in Foo() do() // Internal error here 
+0

Merci pour la réponse, Tomas! En fait, j'ai trouvé ce bug et je l'ai envoyé à [email protected] il y a une heure ... – ControlFlow

+0

@ControlFlow: Ah, sûrement, ils vont le réparer plus rapidement s'ils reçoivent deux rapports en une seule heure :-)! –

3

Je ne pense pas, mais ce n'est pas un message d'erreur très utile. Voir la section Sequence Iteration Expressions de la spécification pour les détails sur la façon dont les expressions for ... in ... do ... sont évaluées. Si le type implémente IEnumerable<_>, le modèle fonctionne comme prévu. Sinon, le compilateur recherche une méthode publique (la spécification dit "accessible") GetEnumerator avec la bonne signature et l'appelle. Parce que les implémentations d'interface F # sont explicites, la méthode GetEnumerator n'est pas disponible sans monter votre type Foo à IEnumerable. Si vous effectuez la upcast, votre code fonctionne à nouveau comme prévu:

for x in (xs :> Collections.IEnumerable) do() // fine 
5

votre échantillon peut être simplifié à

type Foo() = 
interface Collections.IEnumerable with 
    member x.GetEnumerator() = null 

for x in Foo() do() 

Initialement F # compilateur essaie d'affirmer que le type de source est mise en œuvre IEnumerable <_> Après cette affirmation est échoué - il recherche accessible méthode GetEnumerator/0 qui retourne le type avec MoveNext accessible()/membres actuels.Il semble que les méthodes de mise en œuvre explicite de IEnumerable sont ne sont pas visibles dans le type Foo, parce que le code ci-dessous est valide:

open System 
open System.Collections 

type Foo() = 
    member x.GetEnumerator() : IEnumerator = null 

for x in Foo() do() // GetEnumerator is accessible in Foo 

ou

open System 
open System.Collections 

type Foo() = 
    interface IEnumerable with 
     member x.GetEnumerator() : IEnumerator = null 

for x in (Foo() :> IEnumerable) do() // IEnumerable has accessible GetEnumerator 
+0

Merci pour l'explication! – ControlFlow