A C# implementation of the CallStream pattern

(c) Bertrand Le Roy Dusan published this interesting post a couple of weeks ago about a novel JavaScript chaining pattern:

http://dbj.org/dbj/?p=514

It’s similar to many existing patterns, but the syntax is extraordinarily terse and it provides a new form of friction-free, plugin-less extensibility mechanism.

Here’s a JavaScript example from Dusan’s post:

CallStream("#container")
(find, "div")
(attr, "A", 1)
(css, "color", "#fff")
(logger);

The interesting thing here is that the functions that are being passed as the first argument are arbitrary, they don’t need to be declared as plug-ins. Compare that with a rough jQuery equivalent that could look something like this:

$.fn.logger = function () { /* ... */ }
$("selector")
    .find("div")
    .attr("A", 1)
    .css("color", "#fff")
    .logger();

There is also the “each” method in jQuery that achieves something similar, but its syntax is a little more verbose.

Of course, that this pattern can be expressed so easily in JavaScript owes everything to the extraordinary way functions are treated in that language, something Douglas Crockford called “the very best part of JavaScript”.

One of the first things I thought while reading Dusan’s post was how I could adapt that to C#. After all, with Lambdas and delegates, C# also has its first-class functions.

And sure enough, it works really really well. After about ten minutes, I was able to write this:

CallStreamFactory.CallStream
  (p => Console.WriteLine("Yay!"))
  (Dump, DateTime.Now)
  (DumpFooAndBar, new { Foo = 42, Bar = "the answer" })
  (p => Console.ReadKey());

Where the Dump function is:

public static void Dump(object options) {
    Console.WriteLine(options.ToString());
}

And DumpFooAndBar is:

public static void DumpFooAndBar(dynamic options) {
    Console.WriteLine("Foo is {0} and bar is {1}.",
options.Foo, options.Bar); }

So how does this work? Well, it really is very simple. And not. Let’s say it’s not a lot of code, but if you’re like me you might need an Advil after that. First, I defined the signature of the CallStream method as follows:

public delegate CallStream CallStream
(Action<object> action, object options = null);

The delegate define a call stream as something that takes an action (a function of the options) and an optional options object and that returns a delegate of its own type. Tricky, but that actually works, a delegate can return its own type.

Then I wrote an implementation of that delegate that calls the action and returns itself:

public static CallStream CallStream
(Action<object> action, object options = null) {
action(options); return CallStream; }

Pretty nice, eh? Well, yes and no. What we are doing here is to execute a sequence of actions using an interesting novel syntax. But for this to be actually useful, you’d need to build a more specialized call stream factory that comes with some sort of context (like Dusan did in JavaScript).

For example, you could write the following alternate delegate signature that takes a string and returns itself:

public delegate StringCallStream
StringCallStream
(string message);

And then write the following call stream (notice the currying):

public static StringCallStream
CreateDumpCallStream(string dumpPath) {
StringCallStream str = null; var dump = File.AppendText(dumpPath); dump.AutoFlush = true; str = s => { dump.WriteLine(s); return str; }; return str; }

(I know, I’m not closing that stream; sure; bad, bad Bertrand)

Finally, here’s how you use it:

CallStreamFactory.CreateDumpCallStream(@".\dump.txt")
    ("Wow, this really works.")
    (DateTime.Now.ToLongTimeString())
    ("And that is all.");

Next step would be to combine this contextual implementation with the one that takes an action parameter and do some really fun stuff.

I’m only scratching the surface here. This pattern could reveal itself to be nothing more than a gratuitous mind-bender or there could be applications that we hardly suspect at this point. In any case, it’s a fun new construct. Or is this nothing new? You tell me… Comments are open :)

6 Comments

  • Right now I do think this is an meta pattern. Which always is an "gratuitous mind-bender" indeed ;)
    An concept, on which family of concepts can be based.
    And each member of this family provides base for some particular design and implementation.

    I am sure you are thinking of next "FluentPath" release, exhibiting a new interface mechanism, based on a new design, that is based on the CallStream concept ;)

    --DBJ (aka : Dusan)

  • A little crazy, but interesting; I hadn't seen that!

    Here is a completely different approach that would support square brackets instead of parenthesis (why? I don't know, why now?!)

    class CallStreamFactory {
    TextWriter _dump;
    public static CallStreamFactory CreateDumpCallStream(string dumpPath) {
    var dump = File.AppendText(dumpPath);
    dump.AutoFlush = true;
    return new CallStreamFactory() { _dump = dump };
    }

    public CallStreamFactory this[string s] {
    get {
    _dump.WriteLine(s);
    return this;
    }
    }
    }

    and then:

    var ignoreMe = CallStreamFactory.CreateDumpCallStream(@".\dump.txt")
    ["Wow, this really works."]
    [DateTime.Now.ToLongTimeString()]
    ["And that is all."];

    Ok, the ignoreMe part is funky, but you were supposed to ignore it... :)

  • @david: your version is interesting because it's less convoluted, and also because it does allow one thing that is also allowed from JavaScript: having other methods hanging off the call stream object.

  • @David: oh, and there's no good reason in your version to keep stuff static. Rename that factory to CallStream, make the Create method the constructor and you've got something a lot saner than my version...

  • For the C# variant of the CallStream Bertran has provided the key implementation idiom :

    public delegate CallStream CallStream
    (Action action, object options = null);

    Which is the closest one can get to the CallStream "functional" nature. No classes, please ;o)

    --DBJ

  • @Roland: yes, that should be pretty natural in F#.

Comments have been disabled for this content.