Functional Programming via Anonymous Delegates and Script#

This post demonstrates how you can use anonymous delegates from C# and Script# to implement functional programming patterns in JavaScript.

Joel Spolsky published an article around functional programming, and how it enables things like separating the logic that traverses a data structure, from the code that needs to perform some logic over each object in the data structure. He uses JavaScript, the ability to define anonymous functions and using them as parameter values in his samples to illustrate how it provides a better model compared to languages like Java. In his post, he asks "Can your programming language do this?" Check it out, and then come back here...

So, it turns out C# 2.0 already supports some degree of functional programming via anonymous delegates. C# doesn't just provide the ability to pass a function as a parameter value. The C# feature is built on top of the existing delegate infrastructure, which allows one to associate object (or "this") context with the function when needed for instance methods, as well as allow a compiler to check if the signature of your function matches the expected parameter type. I was not a huge fan of anonymous delegates, but things seem to have changed... a bit. I guess working with JavaScript for some time now has opened me somewhat to using them, esp. when they seem appropriate.

As a result, I just added support for anonymous delegates in Script# as well (as of build 0.1.4.0). I also added iteration APIs such as ForEach, Map, Filter, Some and Every on Array. For example, Map allows you to map each element of an array to some other computed value. Here is what Script# defines in its mscorlib equivalent, sscorlib.dll:

public delegate object ArrayItemMapCallback(object value, int index, Array array);
public delegate int CompareCallback(object x, object y);
public class Array {
    public Array Map(ArrayItemMapCallback callback);
    public void Sort(CompareCallback compareCallback);
}
Now I can write some code in my app as follows:
static int SortNumbers(object x, object y) { ... }
static void MyMethod() {
    int[] values = new int[] { 3, 1, 2 };

    values.Sort(new CompareCallback(SortNumbers));
    int[] computedValues =
        (int[])values.Map(delegate(object value, int index, Array array) {
            return (int)value * (int)value;
        });
}
This translates to the following JavaScript:
function sortNumbers(x, y) { ... }
function MyMethod() {
    var values = [ 3, 1, 2 ];
    values.Sort(Delegate.create(null, sortNumbers));
    var computedValues = values.map(Delegate.create(null, function(value, index, array) {
        return value * value;
    });
}

The sample shows how you can use delegates to point to named member methods, as well as anonymous methods written inline within another function. Note that once I add generics support to Script#, this will be even more clearer when the casts all disappear. Personally, I like the strong typing associated with delegates; For example, I don't have to explicitly remember the signature and ordering of parameters in the callback method. Now the compiler can check if I am doing the right thing up front and do its job, rather than having to wade through runtime errors myself to tell me the same.

Script# doesn't limit usage of anonymous delegates to intrinsic JavaScript types such as Array. As you'd expect, you can use the anonymous delegate functionality anywhere you use delegates. The Script# runtime provides a light-weight implementation of Delegate written in JavaScript.

For example, one of the classes I provide in the Script# framework is HTTPRequest, which is an abstraction over XMLHttpRequest. The following is relevant bits of API it provides:

public delegate void HTTPRequestCompletedCallback(HTTPRequest request, object userContext);
public class HTTPRequest {
    public static HTTPRequest Create(string uri, string verb);
    public void Invoke(HTTPRequestCompletedCallback callback, object userContext);
}
Now, say I have an instance method in my application class, and inside that I could use the API as follows (this is based on one of the shipping samples):
private void Update() { ... }

void PerformRequest() {
    HTTPRequest request = HTTPRequest.CreateRequest("MyService.ashx", "GET");
    request.Invoke(delegate(HTTPRequest request, object context) {
        string text = request.Response.Text;
        this.Update();
    }, null);
}
This gets translated to:
function update() { ... }

function performRequest() {
    var request = ScriptFX.Net.HTTPRequest.createRequest("MyService.ashx", "GET");
    request.invoke(Delegate.create(this, function(request, context) {
        var text = request.get_response().get_text();
        this.update();
    }, null);
}

Notice the distinction from the previous sample; the anonymous delegate is defined inside an instance method rather than inside a static method. Hence the delegate is passed in the "this" context, and it can be used inside its body.

What do you think? Cool, huh? :-) These allow you to use some key functional aspects of functional programming in JavaScript even while using c# and script#. You can download the bits to try things out and follow future developments in Script# from my project page.

Quick heads up for existing Script# users:
In past builds, one needed to call Delegate.Unwrap on a delegate instance before passing it onto a native script API or into the DOM. As of build 0.1.4.1 (published today), you can now skip this step. A delegate is directly usable as a function. This has an additional benefit; where previously APIs on things like Array.Sort, DOMElement.AttachEvent took an untyped, generic Function instance, they now take strongly-typed delegates like CompareCallback, and DOMEventHandler.

Posted on Thursday, 8/3/2006 @ 10:41 AM | #Script#


Comments

8 comments have been posted.

Drew Marsh

Posted on 8/3/2006 @ 11:52 AM
You should consider using generics on these methods as well if you really want to make them powerful. Taking object and Array and having to cast is "old skewl" now. ;) While I realize the typing has no translation in JavaScript it still provides the best possible model for the person working in Script# because they won't have to do all the silly casting which also gets thrown out in the translation anyway.

For example, you can change your signatures to be:

public delegate TItemType ArrayItemMapCallback<TItemType>(TItemType value, int index, TItemType[] array);
public delegate int CompareCallback<TItemType>(TItemType x, TItemType y);
public class Array
{
public TItemType[] Map<TItemType>(ArrayItemMapCallback<TItemType> callback);
public void Sort<TItemType>(CompareCallback<TItemType> compareCallback);
}


Just a thought. :)

Cheers,
Drew

Nikhil Kothari

Posted on 8/3/2006 @ 12:24 PM
As I mentioned in my post, "Once I add generics support..."

Max Metral

Posted on 8/4/2006 @ 5:37 PM
I continue to be incredibly impressed at the activity around Atlas/Script#/Control Toolkit. Let's face it, Microsoft is not the mecca of development talent it once was. Between its own success and google, it's been a tough couple years. But I've always thought the tremendous weapon was the understanding of developer needs over all the rest. PS2 vs. XBox, Windows DRM vs. Real, Mac vs. Win32; they all were simply no contest in terms of development tools (PS2 vs. XBox isn't won yet, but someday). I've been disappointed at things like VS Team System, because it starts to play to Perforce/Oracle game, and I don't think it's the right time for that. It's exactly the right time for things like Atlas/Script# and I hope they realize how crucial you and your team are to the long term dominance of even .Net and ASP.Net. Part of me wants Script# as part of the core platform, part of me realizes that train can never keep up with the innovation required for these products (much like I worry about MSBuild). Anyhow, keep up the good work and congratulations on industry-significant innovation at a time when it couldn't be more necessary.

Ron Buckton

Posted on 8/5/2006 @ 9:20 PM
Nikhil I am definately waiting for generics support. I am a big fan of LINQ and what it can do (see my blog for a series i've started about LINQ and similar techniques for 2.0). I have a pretty well-defined javascript library of iterator/sequence calls that mimic linq (some of the same features supported by my rockside project that I mention) that I'd be happy to share WRT some of those methods.

With the way that delegates work in C# 2.0 and the forthcoming 3.0, "delegate types" are starting to seem just like bulky and unnecessary syntax. using Func<..., T> and the ECMA TR for Action<...> almost all delegate types are covered.

Ron Buckton

Posted on 8/5/2006 @ 9:24 PM
There are days where I wish that Script# was a user-contributable project on CodePlex since it's not yet part of the Atlas product. I'd love to contribute time and energy to extending Script# functionality (either in the core language and compiler features to extensions such as controls, Atlas integration (e.g. creating Script# interfaces for Atlas core classes), etc.

Nikhil, is there any possiblity of opening this up or is this getting more likely to be rolled into Atlas as it becomes more mature?

Nikhil Kothari

Posted on 8/6/2006 @ 6:29 PM
Ron, I am planning on generics support as a v-next'ish feature... there are a number of key higher pri things to get done in the framework.

Speaking of contributing to the framework, its great to hear you are interested. I was in fact hoping the community would extend and share their work. I am still figuring out the details in how best to share the code for the framework - esp. since it ships in JavaScript form, and theres nothing really special in the C# code. I am going to add key building blocks for UI components in the next couple of releases, which should facilitate things a bit.

However, I don't think you should be blocked in any way - the script# framework can be extended by other developers. Anyone can actually do whatever I might do - both in the framework and in the runtime. The pdf document in the downloadable zip file explains the two kinds of assemblies, and how you can go about building either of them.

Let me know if you have particular questions, or want to take the next steps for contributing - maybe use my contact form for further offline discussion on the subject.

Scott

Posted on 8/8/2006 @ 9:08 AM
Ron -

The framework is pretty easy to build on. There's lots of thing you can do , for instance I created a middle compiler for System.Collections that translates cs code not supported in script# to cs code supported in script#.

Seems like generics should be pretty easy for javascript built in types since they aren't typed anyways, I can't figure out how you would get around other classes though. Nikhil, have you considered creating a base object class similar to c# base object that everything inherits from and translating c# builtin types to custom objects/structs instead of javascript builtin types?. Do you think there would be too much of an overhead cost? That was one of the workarounds I considered when I was adding on to the compiler.

C#
int myInt = 4;

Translates to:
var myInt = new ScriptFXInt(4);

Nikhil Kothari

Posted on 8/26/2006 @ 1:30 AM
Scott, one of the goals was specifically to keep the abstraction count down - hence rather than have things like ScriptFXInt, and create a proliferation of types in the script type system, I chose to redefine the primitive types in C#/CLR, by virtue of having a custom mscorlib... this is because the &amp;quot;int&amp;quot; keyword in c# always maps to System.Int32 ... changing the definition of Int32, and System.Object etc. was the only way to create a reasonable experience - otherwise you couldn't use the int keyword for example.
The discussion on this post has been closed. Please use my contact form to provide comments.