24

I'm trying to apply Seq functions on an IEnumerable. More specifically, it's System.Windows.Forms.HtmlElementCollection which implements ICollection and IEnumerable.

Since an F# seq is an IEnumerable, I thought I could write

let foo = myHtmlElementCollection |> Seq.find (fun i -> i.Name = "foo")

but the compiler will have none of it, and complains that "The type 'HtmlElementCollection' is not compatible with the type 'seq<'a>'".

However, the compiler willingly accepts the IEnumerable in a for .. in .. sequence expression:

let foo = seq { for i in myHtmlElementCollection -> i } |> Seq.find (fun i -> i.Name = "foo")

What do I need to do in order for the IEnumerable to be treated just like any old F# seq?

NB: This question is marked as a duplicate, but it's not. The linked "duplicate" is about untyped sequences (a seq from the seq { for ... } expression), whereas my question is about typed sequences.

John Reynolds
  • 4,927
  • 4
  • 34
  • 42
  • Not quite, since he gets a seq from the seq { for ... } expression, whereas I get a typed seq. – John Reynolds Jan 31 '14 at 01:00
  • 4
    That's because [§6.5.6 of the spec](http://fsharp.org/about/files/spec.pdf). The for loop knows that your `HtmlElementCollection` __has an Item property with a more specific return type__, ie. `HtmlElement`. – kaefer Jan 31 '14 at 05:58

1 Answers1

42

F#'s type seq<'a> is equivalent to the generic IEnumerable<'a> in System.Collections.Generic, but not to the non-generic IEnumerable. You can use the Seq.cast<'a> function to convert from the non-generic one to the generic one:

let foo = myHtmlElementCollection
            |> Seq.cast<HtmlElement>
            |> Seq.find (fun i -> i.Name = "foo")
Ganesh Sittampalam
  • 28,821
  • 4
  • 79
  • 98
  • 1
    Thanks, that's one mystery less. I'm still puzzled why `seq { for i in myHtmlElementCollection -> i }` works and gives me a seq. The HtmlElementCollection.Item property is typed, so I suspect Item is used somehow, but I would have thought that `seq { for ... }` would use an enumerator rather than ICollection.Count and Item.[i]. – John Reynolds Jan 31 '14 at 00:57
  • 1
    This is an incredibly useful answer. I had been struggling to understand why (when s is a string) let xs = [for c in s -> match c with | '1' -> 1uy | '0' -> 1uy | _ -> 0uy ] would not work - the documentation doesn't make the IEnumerable vs IEnumerable<'a> distinction clear at all. Coming from a Haskell background this would never have occurred to me. – Jeremy ODonoghue Apr 27 '18 at 12:18