title: F# "for loop" optimization
body: <p>Code example:</p>
  
  <pre><code>let foo1 (arr : int[]) = 
      for i = 0 to arr.Length-1 do
          arr.[i] &lt;- i
  
  let foo2 (arr : int[]) = 
      for i in [0..arr.Length-1] do
          arr.[i] &lt;- i
  </code></pre>
  
  <p>I thought that this functions should be equivalent to each other (in terms of performance). But if we look into IL listing, we'll see:</p>
  
  <p>First function, 15 lines, no dynamic allocations, no <code>try</code> operator, no virtual calling:</p>
  
  <pre><code>IL_0000: nop
  IL_0001: ldc.i4.0
  IL_0002: stloc.0
  IL_0003: br.s IL_0011
  // loop start (head: IL_0011)
      IL_0005: ldarg.0
      IL_0006: ldloc.0
      IL_0007: ldloc.0
      IL_0008: stelem.any [mscorlib]System.Int32
      IL_000d: ldloc.0
      IL_000e: ldc.i4.1
      IL_000f: add
      IL_0010: stloc.0
  
      IL_0011: ldloc.0
      IL_0012: ldarg.0
      IL_0013: ldlen
      IL_0014: conv.i4
      IL_0015: blt.s IL_0005
  // end loop
  
  IL_0017: ret
  </code></pre>
  
  <p>and second one - almost 100 lines, lots of allocations/deallocations, callings of virtual functions, lots of <code>try</code>/<code>Dispose</code>:</p>
  
  <pre><code>IL_0000: nop
  IL_0001: ldc.i4.0
  IL_0002: ldc.i4.1
  IL_0003: ldarg.0
  IL_0004: ldlen
  IL_0005: conv.i4
  IL_0006: ldc.i4.1
  IL_0007: sub
  IL_0008: call class [mscorlib]System.Collections.Generic.IEnumerable`1&lt;int32&gt; [FSharp.Core]Microsoft.FSharp.Core.Operators/OperatorIntrinsics::RangeInt32(int32, int32, int32)
  IL_000d: call class [mscorlib]System.Collections.Generic.IEnumerable`1&lt;!!0&gt; [FSharp.Core]Microsoft.FSharp.Core.Operators::CreateSequence&lt;int32&gt;(class [mscorlib]System.Collections.Generic.IEnumerable`1&lt;!!0&gt;)
  IL_0012: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1&lt;!!0&gt; [FSharp.Core]Microsoft.FSharp.Collections.SeqModule::ToList&lt;int32&gt;(class [mscorlib]System.Collections.Generic.IEnumerable`1&lt;!!0&gt;)
  IL_0017: stloc.0
  IL_0018: ldloc.0
  IL_0019: unbox.any class [mscorlib]System.Collections.Generic.IEnumerable`1&lt;int32&gt;
  IL_001e: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1&lt;!0&gt; class [mscorlib]System.Collections.Generic.IEnumerable`1&lt;int32&gt;::GetEnumerator()
  IL_0023: stloc.1
  .try
  {
      // loop start (head: IL_0024)
          IL_0024: ldloc.1
          IL_0025: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
          IL_002a: brfalse.s IL_003e
  
          IL_002c: ldloc.1
          IL_002d: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1&lt;int32&gt;::get_Current()
          IL_0032: stloc.3
          IL_0033: ldarg.0
          IL_0034: ldloc.3
          IL_0035: ldloc.3
          IL_0036: stelem.any [mscorlib]System.Int32
          IL_003b: nop
          IL_003c: br.s IL_0024
      // end loop
  
      IL_003e: ldnull
      IL_003f: stloc.2
      IL_0040: leave.s IL_005b
  } // end .try
  finally
  {
      IL_0042: ldloc.1
      IL_0043: isinst [mscorlib]System.IDisposable
      IL_0048: stloc.s 4
      IL_004a: ldloc.s 4
      IL_004c: brfalse.s IL_0058
  
      IL_004e: ldloc.s 4
      IL_0050: callvirt instance void [mscorlib]System.IDisposable::Dispose()
      IL_0055: ldnull
      IL_0056: pop
      IL_0057: endfinally
  
      IL_0058: ldnull
      IL_0059: pop
      IL_005a: endfinally
  } // end handler
  
  IL_005b: ldloc.2
  IL_005c: pop
  IL_005d: ret
  </code></pre>
  
  <p>My question is why does F# compiler uses so complicated code for <code>foo2</code>? Why does it use an <code>IEnumerable</code> to implement so trivial loop?</p>