🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Tangent: Lambda Expressions

Published August 21, 2008
Advertisement
Daerax asked if Tangent was going to support local functions. It seems he meant anonymous delegate things, so yes. They're very useful for just little tidbits of glue code (because there's no 'bind' equivalent in C#), and pretty vital in providing snippets as strategies. Since Tangent is making an increased emphasis on functions over objects, they gain that much more importance.

I went with C# lambda style syntax, since that does tend to be cleaner and a little less ambiguous for the parser than alternatives. Unfortunately, Tangent can't (nicely) support the type inference C# allows on params and on return type. So the Tangent syntax is a bit more verbose:

( method-parameter-list ) => return-type-expression block


It kinda sucks, because the verbosity might push people away from its use. And it will likely turn away functional language advocates who like things nice and succinct. I dislike succinctness in languages, but x => x + 1; is pretty damned clear.

Making the lambdas work required only a few changes. The syntax itself is constructed of bits already used elsewhere. When the lambda's block is compiled, the lambda's scope is simply added to the stack of scopes for name resolution. The name resolution got an added snippet of code to tack on a 'use the outer scope' placeholder (for locals/params/member vars in the outer method). That placeholder then becomes a VM opcode (access outer-scope) during compilation. The lambda itself is compiled into its own VM opcode that creates the method instance and binds the outer scope (params, calling-instance, locals) with it.

This should emulate C#'s closure behavior (and it's a bug if it doesn't), such as weird stuff like this:
void foo(){    int x = 0;    someDelegate = ()=>{Console.WriteLine(x);};    x = 1000;}foo();someDelegate();    // 1000!



And now for some (mildly ugly) test-code:

public class foo{	public delegate bool Conditional(int x);	public delegate void Action(int x);	public void Process(int x){		if( Conditional(x) ){			Action(x);		}	}}public static void main(){	local foo A = new foo;	local foo B = new foo;	A.Conditional = (int x)=>bool{				return(x<10);			};	A.Action = (int x)=>void{				print "A:" x " \r\n";			};	B.Conditional = (int x)=>bool{				return(x==42);			};	B.Action = (int x)=>void{				print "B!\r\n";			};	A.Process(4);	A.Process(42);	B.Process(4);	B.Process(42);}// A:4// B!


Binary parameter lambdas can be used as operators (not recommended) or stored in an operator delegate (more recommended). I will be providing a mechanism for combining them (and other arbitrary methods) into a method group, which should yield the ability to do stuff akin to pattern matching found in functional languages (as well as mildly nifty polymorphism on event-handling).

Next in the queue should be Properties. Though Disgaea 3 is coming out next week. Productivity that doesn't involve joyously evil tactical role playing is likely to decline precipitously.
Previous Entry Tangent: Delegates
Next Entry Tangent: Buggery
0 likes 3 comments

Comments

Daerax
That is not weird! That is beautiful. Those are closures. Your language just keeps getting more and more powerful.

P.S. The syntax is still more than terse enough and not verbose at all. I like it. And Im picky too since as scooby would say, icchh. I hate verbosity.

I see a gotcha in this code. SomeDelegate should technically not be visible outside of foo. It has escaped its scope. I assume you're just testing though.

What does this do?
void foo(){
    int x = 0;
    someDelegate = ()=>{ Console.WriteLine(x); x = x+1};

    x = 1000;
}

foo();
someDelegate();   
August 24, 2008 10:19 AM
Telastyn
Example code. It would not compile. Presume SomeDelegate is declared as a global and the bottom two lines are within some valid method definition.

The assignment in the delegate would modify the instance of x in the instance of foo referenced by the instance of the lambda being executed.

Thus:



public class foo{
    
    public delegate void Execute();

    public void Initialize(){
        local int x = 0;

        Execute = ()=>void{ print x "\n"; x=x+1; }; 

        x = 1000;
   }
}

public static void main(){
    local foo A = new foo;
    local foo B = new foo;

    A.Initialize();
    B.Initialize();

    A.Execute();    // 1000
    B.Execute();    // 1000
    A.Execute();    // 1001
    A.Execute();    // 1002
    B.Execute();    // 1001
}

August 24, 2008 11:12 AM
Daerax
Yup those are closures. Damn thats nice. =)
That will really help with events too.
August 24, 2008 12:23 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement