I'm ashamed.
Well, the good news is, Sencha compiles simple programs like this:
(let ((sq (lambda (x) (* x x)))
(dbl (lambda (x y) (x (x y)))))
(dbl sq 10))
Which demonstrates nothing but good old environment modification and free variable binding.
But it also compiles this:
(letrec ((fib (lambda (x)
(if (<= x 1)
1
(+ (fib (- x 1)) (fib (- x 2)))))))
(fib 12))
Which demonstrates, of course, recurisve let bindings. This isn't what I'm ashamed of.
What I am ashamed of is the nasty IL that gets generated from the latter.
First, the top-level execution:
.method public hidebysig static void Main(string[] A_0) cil managed
{
.entrypoint
// Code size 69 (0x45)
.maxstack 3
IL_0000: newobj instance void __lambda0::.ctor()
IL_0005: dup
IL_0006: ldvirtftn instance object __lambda0::Apply1(object)
IL_000c: newobj instance void class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object>::.ctor(object,
native int)
IL_0011: stsfld class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object> Program::fib
IL_0016: ldsfld class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object> Program::fib
IL_001b: dup
IL_001c: ldc.i4.1
IL_001d: call void [SenchaRuntimeLibrary]Sencha.Runtime.RuntimeHelper::AssertCompatableFunctionType(object,
int32)
IL_0022: castclass class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object>
IL_0027: ldc.r8 12.
IL_0030: box [mscorlib]System.Double
IL_0035: call instance !0 class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object>::Invoke(!1)
IL_003a: call string [SenchaRuntimeLibrary]Sencha.Runtime.RuntimeHelper::ToString(object)
IL_003f: call void [mscorlib]System.Console::WriteLine(string)
IL_0044: ret
} // end of method Program::Main
Notice the obvious areas for optimization... For example, if I construct a new Func1`2 delegate right before I call it... well... do I really need all that crap about reloading and type checking? Likely not.
But it gets worse. Check out the actual fib function IL:
.method public hidebysig virtual instance object
Apply1([in] object x) cil managed
{
.override method instance !0 class [SenchaRuntimeLibrary]Sencha.Runtime.'Closure1`2'<object,object>::Apply1(!1)
// Code size 189 (0xbd)
.maxstack 10
.locals init ([0] object[] V_0,
[1] object[] V_1,
[2] object[] V_2)
IL_0000: ldarg.1
IL_0001: ldc.r8 1.
IL_000a: box [mscorlib]System.Double
IL_000f: call bool [SenchaRuntimeLibrary]Sencha.Runtime.StandardSchemeFunctions::op_LessThanOrEqual(object,
object)
IL_0014: box [mscorlib]System.Boolean
IL_0019: call bool [SenchaRuntimeLibrary]Sencha.Runtime.StandardSchemeFunctions::IsTrue(object)
IL_001e: brfalse.s IL_0033
IL_0020: ldc.r8 1.
IL_0029: box [mscorlib]System.Double
IL_002e: br IL_00bc
IL_0033: ldc.i4.1
IL_0034: newarr [mscorlib]System.Object
IL_0039: stloc V_0
IL_003d: nop
IL_003e: nop
IL_003f: ldsfld class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object> Program::fib
IL_0044: dup
IL_0045: ldc.i4.1
IL_0046: call void [SenchaRuntimeLibrary]Sencha.Runtime.RuntimeHelper::AssertCompatableFunctionType(object,
int32)
IL_004b: castclass class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object>
IL_0050: ldc.i4.1
IL_0051: newarr [mscorlib]System.Object
IL_0056: stloc V_1
IL_005a: nop
IL_005b: nop
IL_005c: ldarg.1
IL_005d: ldloc.1
IL_005e: ldc.i4.0
IL_005f: ldc.r8 1.
IL_0068: box [mscorlib]System.Double
IL_006d: stelem.ref
IL_006e: ldloc.1
IL_006f: call object [SenchaRuntimeLibrary]Sencha.Runtime.StandardSchemeFunctions::op_Sub(object,
object[])
IL_0074: call instance !0 class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object>::Invoke(!1)
IL_0079: ldloc.0
IL_007a: ldc.i4.0
IL_007b: ldsfld class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object> Program::fib
IL_0080: dup
IL_0081: ldc.i4.1
IL_0082: call void [SenchaRuntimeLibrary]Sencha.Runtime.RuntimeHelper::AssertCompatableFunctionType(object,
int32)
IL_0087: castclass class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object>
IL_008c: ldc.i4.1
IL_008d: newarr [mscorlib]System.Object
IL_0092: stloc V_2
IL_0096: nop
IL_0097: nop
IL_0098: ldarg.1
IL_0099: ldloc.2
IL_009a: ldc.i4.0
IL_009b: ldc.r8 2.
IL_00a4: box [mscorlib]System.Double
IL_00a9: stelem.ref
IL_00aa: ldloc.2
IL_00ab: call object [SenchaRuntimeLibrary]Sencha.Runtime.StandardSchemeFunctions::op_Sub(object,
object[])
IL_00b0: call instance !0 class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object>::Invoke(!1)
IL_00b5: stelem.ref
IL_00b6: ldloc.0
IL_00b7: call object [SenchaRuntimeLibrary]Sencha.Runtime.StandardSchemeFunctions::op_Add(object,
object[])
IL_00bc: ret
} // end of method __lambda0::Apply1
Ouch! You mean I have to generate verifiable code and optimize it?!? As each day passes, I respect commercial compiler teams a little more.
In case you're wondering, the roughly equivalent C# code would be:
using System;
class Program
{
delegate double fibfunc(double x);
static fibfunc fib;
static void Main()
{
fib = delegate (double x) {
if (x <= 1)
return 1;
else
return fib(x - 1) + fib(x - 2);
};
Console.WriteLine(fib(12));
}
}
Believe me -- the IL generated is a bit nicer. :)