I can now compile simple Scheme function calls into IL, such as
(+ 5 10)
As well as lambda applications
((lambda (x)
(* x x))
9)
((lambda (x y)
(* x y))
9 3)
And I was just able to compile (a workable copy!) of the following function call. It was a little tricky because the first lambda deals with higher-order functions... i.e. it takes a function as its first argument:
((lambda (x y)
(* (x y) y))
(lambda (x) (* x 2)) 3)
While it may not be immediately obvious, the correct output for this is 18. Indeed, that is what gets printed. :)
Below is the IL that gets generated by my compiler for this last program. Notice the hideous overuse of boxing. This will change. I already have a bunch of so-called “fast” entry points for all of the standard Scheme functions, and I intend to do some trickery with the lambda closure classes. Since Scheme doesn't have static typing, anything I introduce is just an optimization, and will have to rely solely on the use of literals to infer the type. Everything else has to be run-time type checked, which means objects and boxed value types everywhere. I might be able to get a little clever here, but truthfully haven't spent enough time thinking about it.
Lambdas are represented as Closure objects which have corresponding Function delegates. This is how procedure calls to lambdas occur. You might be able to discern from the IL that evaluation of a lambda generates the new class, instantiates it (passing any free variables to its constructor), and leaves a delegate on the stack. This will not be changing... (Unless I find that performance is poor, of course. From some benchmarking, however, it seems that our Whidbey delegate invocation improvements reduce the overhead versus a straight virtually dispatched call to just under 10%.) One optimization I will certainly be making however, is avoiding the creation of a Closure-derived class altogether for simple lambdas without free variables. In this case, it'll just end up as a static method.
Anyhow, here's the code. Sorry I don't have time to go into great detail right now.
.class public auto ansi Program
extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] A_0) cil managed
{
.entrypoint
// Code size 59 (0x3b)
.maxstack 4
IL_0000: newobj instance void __lambda0::.ctor()
IL_0005: dup
IL_0006: ldvirtftn instance object __lambda0::Apply2(object,
object)
IL_000c: newobj instance void class [SenchaRuntimeLibrary]Sencha.Runtime.'Func2`3'<object,object,object>::.ctor(object,
native int)
IL_0011: newobj instance void __lambda1::.ctor()
IL_0016: dup
IL_0017: ldvirtftn instance object __lambda1::Apply1(object)
IL_001d: newobj instance void class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object>::.ctor(object,
native int)
IL_0022: ldc.r8 3.
IL_002b: box [mscorlib]System.Double
IL_0030: call instance !0 class [SenchaRuntimeLibrary]Sencha.Runtime.'Func2`3'<object,object,object>::Invoke(!1,
!2)
IL_0035: call void [mscorlib]System.Console::WriteLine(object)
IL_003a: ret
} // end of method Program::Main
.method public specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class Program
.class public auto ansi sealed __lambda0
extends class [SenchaRuntimeLibrary]Sencha.Runtime.'Closure2`3'<object,object,object>
{
.method public hidebysig virtual instance object
Apply2([in] object x,
[in] object y) cil managed
{
.override method instance !0 class [SenchaRuntimeLibrary]Sencha.Runtime.'Closure2`3'<object,object,object>::Apply2(!1,
!2)
// Code size 14 (0xe)
.maxstack 2
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: call instance !0 class [SenchaRuntimeLibrary]Sencha.Runtime.'Func1`2'<object,object>::Invoke(!1)
IL_0007: ldarg.2
IL_0008: call object [SenchaRuntimeLibrary]Sencha.Runtime.StandardSchemeFunctions::Multiply(object,
object)
IL_000d: ret
} // end of method __lambda0::Apply2
.method public specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call instance void class [SenchaRuntimeLibrary]Sencha.Runtime.'Closure2`3'<object,object,object>::.ctor()
IL_0006: ret
} // end of method __lambda0::.ctor
} // end of class __lambda0
.class public auto ansi sealed __lambda1
extends class [SenchaRuntimeLibrary]Sencha.Runtime.'Closure1`2'<object,object>
{
.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 21 (0x15)
.maxstack 2
IL_0000: ldarg.1
IL_0001: ldc.r8 2.
IL_000a: box [mscorlib]System.Double
IL_000f: call object [SenchaRuntimeLibrary]Sencha.Runtime.StandardSchemeFunctions::Multiply(object,
object)
IL_0014: ret
} // end of method __lambda1::Apply1
.method public specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call instance void class [SenchaRuntimeLibrary]Sencha.Runtime.'Closure1`2'<object,object>::.ctor()
IL_0006: ret
} // end of method __lambda1::.ctor
} // end of class __lambda1