6

Motivation

The main idea is to explore and understand the limits of how far one can go with the basic LINQ primitives (Select, SelectMany, Concat, etc.). These primitives can all be considered functional operations on a theoretical sequence type. Taking examples from Haskell:

  • Select 'lifts' a function into the sequence (like fmap in Haskell)
  • Concat is composition
  • Aggregate is the sequence type's catamorphism (fold)
  • SelectMany 'extracts' information from the sequence (the Monad bind, >>= operation)
  • etc. (and I'm sure there are better abstractions for the above)

So the question is whether or not the basic sequence (Enumerable) operations in C# are enough to construct an infinite sequence. More concretely it is the following problem:

Problem

I'm curious to know if there's a way to implement something equivalent to the following, but without using yield:

IEnumerable<T> Infinite<T>()
{
    while (true) { yield return default(T); }
}

Is it possible to do sue using the built-in LINQ operators only?

The short answer is that theoretically yes, but practically not because of how Linq is implemented (causing stack overflows).

That's why here are less restrictive rules:

Rules

Alternatively a less restrictive question would go by the rules

  1. You can't use the yield keyword directly
  2. Use only C# itself directly - no IL code, no constructing dynamic assemblies etc.
  3. You can only use the basic .NET lib (only mscorlib.dll, System.Core.dll? not sure what else to include). However if you find a solution with some of the other .NET assemblies (WPF?!), I'm also interested.
  4. Don't implement IEnumerable or IEnumerator.

Notes

An example theoretically correct definition is:

IEnumerable<int> infinite = null;
infinite = new int[1].SelectMany(x => new int[1].Concat(infinite));

This is "correct" but hits a StackOverflowException after 14399 iterations through the enumerable (not quite infinite).

I'm thinking there might be no way to do this due to the C#'s compiler lack of tail recursion optimization. A proof would be nice :)

sinelaw
  • 221
  • 3
  • 8
  • 5
    Rule 4 doesn't make any sense to me. You're basically asking “is there something in the .Net library I can abuse to do this?” – svick Nov 07 '13 at 08:23
  • 1
    I'm curious, how would you do this if you had tail call optimization? Your current implementation doesn't seem tail recursive to me. – svick Nov 07 '13 at 08:41
  • 3
    *do this due to the CLR's lack of tail recursion optimization.* The CLR does *not* lack tail call optimization. It's the C# compiler that lacks support for it. It's even explained in the question you linked. – sloth Nov 07 '13 at 10:54
  • 1
    Does `Don't implement IEnumerable` include `Don't implement IEnumerable`? – sloth Nov 07 '13 at 10:59
  • 1
    Also, your question title is misleading. It should not contain the word *implement* if you actually don't want to implement the interface, but rather use existing implementations. – sloth Nov 07 '13 at 11:14
  • 2
    Why are you doing this? If something has no practical purpose and only serves to obfuscate the meaning of the code, it might be a better fit for codegolf ;) – Phoshi Nov 07 '13 at 11:45
  • 3
    `SelectMany`, along with almost every Linq query, uses `yield` in its implementation. That's what it's there for. If you don't want to use it, you're probably going to have to disregard Linq altogether. Unless your restrictions are "I can't write out the `yield` myself, but I can use other things that use it". Which would be a weird set of restrictions. – KChaloux Nov 07 '13 at 13:32
  • @大师燈XiHuan, you're right, it's the C# compiler not the CLR (there's a `.tail` command) - I was not being exact – sinelaw Nov 07 '13 at 14:48
  • @KChaloux as I said in the first line of the question, it's a theoretical riddle and not anything that's practically useful – sinelaw Nov 07 '13 at 14:49
  • 1
    @sinelaw I think what I'm getting at is that the answer to the riddle depends on what you consider "using yield" to mean. I'd argue that using `SelectMany` _is_ using `yield`, albeit indirectly. – KChaloux Nov 07 '13 at 15:13
  • One, this is probably off topic. Two, you are using yield in your attempt since your code uses yield in its implementation. – Rig Nov 07 '13 at 16:15
  • @KChaloux, it's a matter of taste. I was looking for something like [svick's answer](http://programmers.stackexchange.com/a/216769/20189) below - using only library primitives (although internally they may be using yield) – sinelaw Nov 07 '13 at 16:31
  • I've updated the question to explain the motivation – sinelaw Nov 07 '13 at 16:50

4 Answers4

12

Even if your assertion was true, proving it would be infeasible, because the proof would have to go through all implementations of IEnumerable in the framework and prove for each one of those that it can't be infinite.

And your assertion actually isn't true, there is at least one implementation of IEnumerable in the framework that can be infinite: BlockingCollection.GetConsumingEnumerable():

What you would do is to create a bounded BlockingCollection that's filled in an infinite loop from a separate thread. Calling GetConsumingEnumerable() will then return an infinite IEnumerable:

var source = new BlockingCollection<int>(boundedCapacity: 1);
Task.Run(() => { while (true) source.Add(1); });
return source.GetConsumingEnumerable();
svick
  • 9,999
  • 1
  • 37
  • 51
  • thanks - that does it. "I think there might not be" was more of a guess than an assertion, and you proved it wrong. – sinelaw Nov 07 '13 at 14:53
  • By the way, I still think that using the basic LINQ primitives it isn't possible exactly because the compiler limitation on tail call optimizations - any composition of the basic linq primitives that would result in an infinite sequence would hit a stack overflow – sinelaw Nov 07 '13 at 16:33
  • @sinelaw I still don't see how the tail call optimization would help with that, it's not something that magically fixes all stack overflows. – svick Nov 07 '13 at 17:02
  • Won't this answer run out of memory fairly quickly? BlockingCollection is new to me, but as best I can tell it's a collection: http://msdn.microsoft.com/en-us/library/dd267312.aspx – ta.speot.is Nov 07 '13 at 22:19
  • @ta.speot.is Yes, it's a collection and iterating `GetConsumingEnumerable()` consumes (removes) items from it. So, no, it will never run out of memory. – svick Nov 07 '13 at 23:13
  • @svick Does `GetConsumingEnumerable` consume all the items immediately? Because if it depends on something enumerating the enumerator as fast as your `while (true)` loop adds them ... when that doesn't happen ... where do those queued items get stored? – ta.speot.is Nov 07 '13 at 23:14
  • @ta.speot.is At any moment, there is at most one item queued (that's what the `1` passed to the constructor means). When that 1 item is queued and you call `Add()` again, it blocks until the queued item is consumed. So, the `while` loop adds the items as fast as they are consumed (or slower). – svick Nov 07 '13 at 23:18
  • 1
    Then this is an elegant solution. – ta.speot.is Nov 07 '13 at 23:27
6

Sure. IEnumerable is basically just a call to IEnumerator. Implement an IEnumerator where the MoveNext function just sets an internal value to a random value, and Current returns that random value.

jmoreno
  • 10,640
  • 1
  • 31
  • 48
0

Following, and similar, functions have logarithmic stack growth, and for any practical purposes can be thought as not causing stack overflows:

public static partial class Extensions
{
  public static IEnumerable<T> Infinite<T>(this IEnumerable<T> source) =>
    source.Concat(new[] { source.Concat(source) }.SelectMany(Infinite));
}

-1

Here is a practically infinite iterator:

using System;
using System.Linq;

public class Test
{
    public static void Main()
    {
        var infiniteIterator =
            Enumerable.Range(Int32.MinValue, Int32.MaxValue)
                      .SelectMany(i => Enumerable.Range(Int32.MinValue, Int32.MaxValue))
                      .SelectMany(i => Enumerable.Range(Int32.MinValue, Int32.MaxValue))
                      .SelectMany(i => Enumerable.Range(Int32.MinValue, Int32.MaxValue))
                      .Select(i => default(int));

        foreach (var infinite in infiniteIterator)
            Console.WriteLine(infinite);
    }
}
ta.speot.is
  • 325
  • 1
  • 2
  • 8
  • 1
    For all practical purposes, it is. You could go even further by composing `SelectMany` in a loop. But it will never be infinite. – sinelaw Nov 07 '13 at 04:15
  • I'm beginning to think the answer is "no, it isn't possible due to lack of tail recursion optimization and the way the compiler handles IEnumerable" – sinelaw Nov 07 '13 at 04:31
  • Enumerable.Range is implemented using yield. – Den Nov 07 '13 at 16:52
  • 2
    This sequence has `17179869184` items in it. That is not infinite. – Servy Nov 07 '13 at 18:46
  • @Den That's not incorrect but it doesn't matter: *You can't use the yield keyword directly* – ta.speot.is Nov 07 '13 at 22:15