I node something (Bout You)

Unless you live in a hole you have probably heard of node.js so Ill not bother to explain what it is or what it does. An interesting project has come to light lately, namely Edge.js. The Edge.js project allows you to connect node.js with .Net. The creator of Edge.js Tomasz Janczuk sums this up nicely:

An edge connects two nodes This edge connects node.js with .NET

Currently Edge.js is only available on Windows but there is work underway to bring this to Mono, thus opening up the possibilities even further.

The coding model for Edge.js offers different integration options depending on the quantity of code you are writing, and whether you want to call a .Net dll directly.

Here are a few examples:

Single line lambda expressions:

1
2
3
4
5
6
7
8
9
var edge = require('edge');
var hello = edge.func(
'async (input) => { return ".NET welcomes " + input.ToString(); }'
);

hello('Node.js', function (error, result) {
if (error) throw error;
console.log(result);
});

Multi line lambda expressions:

1
2
3
4
5
6
7
var hello = require('edge').func(function () {/*
async (input) => {
return ".NET welcomes " + input.ToString();
}
*/});


hello('Node.js', function (error, result) { ... });

File based expresions:

1
2
3
var hello = require('edge').func('hello.csx');

hello('Node.js', function (error, result) { ... });

Invoking via a dll:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
var add7 = require('edge').func('My.Sample.dll');

add7(12, function (error, result) { ... }
```

The entry point into your .NET code is a delegate normalized to a `Func<object,Task<object>>`. This allows node.js code to call the .NET code asynchronously and avoid blocking the node.js event loop. If you think about the possibilities of this for a moment, a lot of different options begin to open up with this framework. I can foresee a lot of interesting things appearing in the future.

There are currently two .Net compilers part of Edge.js. A C# based compiler and an [IronPython][5] one. You can probably guess what I'm going say next...
- - -
##Introducing Edge-fs - An F# complier for edge.js

First let's look at the interop model for Edge.js:
[{% img https://f.cloud.github.com/assets/822369/234085/b305625c-8768-11e2-8de0-e03ae98e7249.PNG 300 %}][3]

In summary, if we want to integrate with Edge.js then we must coerce whatever input that is passed to a single delegate function `Func<Object, Task<Object>>`

In terms of the C# Edge compiler a lambda expression is passed in the [async await][6] style:-
{% codeblock lang:csharp%}
async (input) => { return ".NET welcomes " + input.ToString(); }
{%endcodeblock%}

The Python Edge compiler is passed a lambda in it's native format too:

{%codeblock lang:python %}
def hello(input):
return "Python welcomes " + input

lambda x: hello(x)
{%endcodeblock%}

So where does that leave us with F# compiler support? Well, I suppose the most intuitive support for F# would be to use F# async workflow support. This would mean the that lambda expression would look like this:

{% codeblock lang:fsharp %}
fun input -> async{return ".NET welcomes " + input.ToString()}
{%endcodeblock%}

You can see it's not that different from C#'s' async await style syntax, you can really see the F# async workflow heritage here.

Now lets look at how a script file or dll and have a look to see how this would fits:

{% codeblock lang:fsharp%}
namespace global
type Startup() =
let addSeven v = v + 7
member x.Invoke(input:obj) =
let v = input :?> int
async.Return (addSeven v :> obj) |> Async.StartAsTask
{%endcodeblock%}

This is really easy too, the Async module has a StartAsTask function that perfectly fits here.

By default Edge.js looks for a type in the global namespace called `Startup` with a public method called `Invoke`. The invoke method takes a single parameter `input` which is of the type `Object`. The return type of this method is as you might have guessed `Task<Object>`. You can also add parameters to the node.js to indicate the location of the assembly, type and method name using the `assemblyName`, `typeName` and `methodName` parameters respectively.

```js

var clrMethod = edge.func({
assemblyFile: 'My.Edge.Samples.dll',
typeName: 'Samples.FooBar.MyType',
methodName: 'MyMethod'
});

Edge.js has some really good documentation so if your interested then you really should check it out. I plan on supporting all of the calling conventions that the C# edge compiler has to offer. At the moment only the in-line lambdas and the file based inputs have been tested, but I’m working on further examples, and fixes as needed.


As an aside, with dll based inputs any .Net language would work with Edge.js, you don’t need a custom compiler. The internals of Edge.js invoke your dll via reflection.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Handle<v8::Value> ClrFunc::Initialize(const v8::Arguments& args)
{
...
// reference .NET code through pre-compiled CLR assembly
String::Utf8Value assemblyFile(jsassemblyFile);
String::Utf8Value nativeTypeName(options->Get(String::NewSymbol("typeName")));
String::Utf8Value nativeMethodName(options->Get(String::NewSymbol("methodName")));
typeName = gcnew System::String(*nativeTypeName);
methodName = gcnew System::String(*nativeMethodName);
assembly = Assembly::LoadFrom(gcnew System::String(*assemblyFile));
ClrFuncReflectionWrap^ wrap = ClrFuncReflectionWrap::Create(assembly, typeName, methodName);
result = ClrFunc::Initialize(
gcnew System::Func<System::Object^,Task<System::Object^>^>(
wrap, &ClrFuncReflectionWrap::Call));
...

A custom compiler is only required for compiling code in the form of scripts or lambda expressions. It’s expected that this will be a common use case so it’s important to have a native F# compiler support.

So there we have it, a very quick whistle stop tour of Edge-fs the F# compiler for Edge.js. I realise that this post only just skims the surface but I just wanted to get this out in the wild. Ill be updating my repo over the next day or so, and a stable release will go out via the npm package as soon as things stabilise.

Next time we’re going to lift the lid on the F# Edge compiler and take a look at it’s guts, we’ll also go through some of the trials and tribulations I had along the way. Ill also continue the series with some more documentation and samples too.

Until next time!


Essential listening:

  • Alice In Chains - Facelift
  • Pantera - Vulgar Display Of Power