Search code examples
c#f#ienumerableyieldenumerators

F# yield! operator - Implementation and possible C# equivalents


I'm currently learning F# and I really love the yield! (yield-bang) operator. Not only for its name but also for what it does of course.

The yield! operator basically allows you to yield all elements of a sequence from a sequence expression. This is useful for composing enumerators. Since I regularly encounter big, complicated enumerators I am interested in strategies we can use to break them up and compose them from simpler enumerators.

Unfortunatetely, the yield! operator is not available in C#. As far as I understand, what it does is like a foreach (var x in source) yield x; but the book I'm reading (Petricek's Real World F# - Manning) suggests that it has better performance...

  • So what exactly does the F# compiler do here? (Yes, I can look at it using Reflector too but I'd like to have a more detailed description of the mechanism).

In order to achieve a similar construct in C# I have explored multiple ways, but none of them is as concise as the yield! operator and I'm also not sure about the complexity of them. Could someone please provide input if my BigO numbers are correct?

  • Decompose enumerator into multiple private enumerators and then yield each element from the public enumerator:

    foreach (var x in part1()) yield x
    foreach (var x in part2()) yield x
    

    This will effectively result in a "double yield" on each element. Is that O(2n) then? (or possibly worse?) Anyway, using this approach stops me from using yield break; from any of my subparts.

  • Decompose enumerator into multiple private enumerators and then concat all private enumerators from the public enumerator:

    return part1().Concat(part2())
    

    I believe this is no different from the aforementioned solution because Concat() is implemented the way I outlined above.

Any other options?


Solution

  • Regarding how the compiler translates the yield! operation, the paper cited by Thomas Levesque in his answer illustrates one implementation technique in section 4.3 (in particular, their example spanning figures 7-9 is illustrative of the general strategy). I don't think that there's any good way to do this from within an iterator block in C# - as I understand your proposed solutions, they could both result in quadratic behavior when used recursively. You could always manually create a NestedEnumerable<T> subclass to achieve the performance benefits, but this will be quite ugly compared to using a normal iterator block.