Envoy (in Scala, JavaScript, and more)

A little over a decade ago, Eugene Wallingford wrote a paper for the PloP '99 conference, describing the Envoy pattern language, "a pattern language for managing state in a functional program". It's a good read, but the implementation language for the paper is Scheme--given that it's a Lisp dialect, often isn't particularly obvious or easy to understand at first, I thought it might be interesting (both for me and any readers that wanted to follow along) to translate the implementation examples into a variety of different languages. In this case, I thought it would be relatively easy to do it in Scala and F#, given their hybrid object-functional nature, but it's also an interesting exercise to demonstrate it in [Javascript] (I'll use NodeJS v0.8.15, running on my Mac, and Rhino, with the JVM), Yeti (an ML dialect that runs on the JVM), Jaskell (a Haskell dialect that also runs on the JVM), and, hey, what the heck, let's do it in C# while we're at it, just so the .NET guys don't feel too badly outnumbered.

(I'm posting this now with the intent of filling in the Yeti, Jaskell, F# and C# implementations later.)

Note that with lambdas coming in Java 8, it'll be possible to adapt this pattern language to work with Java, too--I'll leave that as exercise to do for myself (and update this blog entry) once I get a Java8 build on the machine on which I'm writing this.

One reason for doing this in Yeti and Jaskell is to demonstrate the original purpose of the Envoy pattern language--that we can achieve object-like semantics even in languages that don't directly support object semantics (like Scheme). But for the other languages, it's fair to ask why anyone would bother doing this in languages that do directly support objects (a la Scala, F#, etc), since it would seem a lot easier to just use the object features directly. And, truth be told, it's true--when looking to model objects in a language that has first-class support for objects, just use that support and those features, and call it a day. The point of this exercise is, for me, to exercise the functional features of those languages, and see exactly how functional languages can provide some of the same benefits that an O-O language enjoys, without having to use the O-O features directly. (There's been a lot of people writing functional-isms in O-O languages, yours truly included, so it seems a good exercise to flip that on its head.) This will also help me figure out where/when/how to use these features, when the need arises.

If you've not yet read the Envoy pattern language, take a moment and do that now; I don't want to annoy Mr. Wallingford in any way by repeating his prose here (not to mention that I'm going to have enough to do as it is just translating the code into several different languages). But I will toss in a brief summary of each of the elements in the pattern language, just so we're all on the same page about what's happening in each of these code samples.

Implementation notes

These are a few notes for each of the implementation langauges.

JavaScript

Because I want to be able to run the JavaScript code on either the Node platform directly or on the Rhino engine (via the Java JDK "jrunscript" command that installs on Java implementations starting with Java 6), and because those two environments provide different mechanisms for printing to the console ("console.log" in Node, and "println" in Rhino), I create a top-level function "out" that aliases to one or the other of those, depending on what's defined in the environment:

var out = (function() { if (typeof(console) !== "undefined" &&
typeof(console.log) !== "undefined") return console.log else if (typeof(println)
!== "undefined") return println else throw new Error("No idea what
to use for output") })();

(This actually gives away one of the punchlines in the first element of the pattern language, Function as Object, below, because here we're pretty clearly using "out" as a function-as-object.)

I used Rhino that ships with Java6, and node v0.8.15 for these.

Scala

I used Scala v2.9.2 running on Java6 for this.

Yeti

Yeti is an ML-based language that compiles to Java bytecode. Unlike Scala, it's a functional-only language (well, sort of), with Hindley-Milner type inference. As the Yeti home page describes, it supports polymorphic structure and variant types, property fields, lazy lists, pattern-matching on values, and a decent interop facility against Java code (meaning it can call Java classes, as well as compile to classes to be called from Java if desired.)

Yeti was at v0.9.7 at the time I wrote this, and again, running on the Java6 VM.

F#

I'm using the Visual Studio 2012 release to write the F# bits, which corresponds to F# 3.0. As far as I can tell, there's nothing really all that "3.0-specific" that I'm using, so it should work with F# 2.0, which shipped with Visual Studio 2010, and there's nothing Windows-specific here either, which means it should run fine on F# 3.0-on-Mono.

Note that, like what I'm doing with the JavaScript version, I'm binding each of the pattern elements into a function for execution, thus creating a scope block that is dissociated from the larger global scope:

let example = fun () -> Console.WriteLine "Howdy world"

If (like I tried once) we were to use the more naive approach:

let example = Console.WriteLine "Howdy world"

... then each of the functions is executed and the results bound to the name described ("example", in this case) at the time the compiler sees it; in other words, each is eagerly-evaluated, instead of waiting to be invoked in the main entry point of the program later. By binding an anonymous function literal, it essentially lazy-fies each of them, and won't execute them until they are deliberately invoked in Main, as in:

[<EntryPoint>] let main argv = example() // ... the others go here
0 // return an integer exit code

With the platform (and prelude) details out of the way, let's begin.

C#

Wow, the C# version is going to be ugly. Let me explain what I mean.

Let's start with the syntax for an anonymous function literal (a lambda, in C# parlance):

() => { return 5; };

This is a function that takes no arguments and yields an int. (Actually, to be exact, this is a delegate, since the lambda wouldn't need an explicit return or the curly braces, since it's a single-expression block and the lambda assumes that the result of the single expression should be implicitly returned.)

Ideally, we'd be able to capture this in an implicitly-typed local variable, like so:

var giveMeFive = () => { return 5; };

But unfortunately, C# doesn't allow this, saying that it "Cannot assign lambda to implicitly-typed local variable". (Doesn't get much more straightforward than that when it comes to an error message.) So, we have to explicitly-type the local variable, which is a Func<> of some type:

Func<int> giveMeFive = () => { return 5; };

Hold on to this thought, because things are going to get even uglier when we want to invoke an anonymous block like this later (when we get into the Closure parts of the pattern language).

Function as Object

In pure functional languages, it's actually difficult to keep state and data tied together--in fact, part of the whole point of a functional language is to write functions that operate on data, ideally on lots of different kinds of data. "Therefore, create a function that acts like an object. Such a function carries the data it needs along with the expression that operates on the data. More importantly, an object encapsulates its data, ensuring that only the allowed operations are applied to them." In other words, by writing a function and keeping the data buried inside of it, we achieve the same kind of encapsulation that object-orientation has traditionally reserved for itself as its principal advantage. This is done via a closure, which is the next element in the language.

Scheme:

The original Scheme implementation looked like this:

(define balance 0) (define withdraw (lambda (balance amount) (if (<=
amount balance) (- balance amount) (error "Insufficient funds" balance))
)) (define deposit (lambda (balance amount) (+ balance amount) )) (define accrue-interest
(lambda (balance interest-rate) (+ balance (* balance interest-rate)) ))

There's a few things wrong with this approach, as Wallingford points out, but to be faithful, recreating this in our target languages is pretty straightforward: three functions, each of which operate on parameters passed in. "You could create new accounts simply by binding values to names. Operating on accounts involves passing the account to the appropriate procedure and binding the new value as appropriate."

JavaScript:

In JavaScript, we can bind function values to names just as we can in Scheme, so it's not actually all that different, once you get past the lack of parentheses and added curly braces. Thus, it looks like:

(function() { out("function-as-object =========") var balance
= 0 var withdraw = function(amount) { if (amount <= balance) balance = balance
- amount else throw new Error("Insufficient funds") } var deposit = function(amount)
{ balance += amount } var accrueInterest = function(interestRate) { balance += (balance
* interestRate) } })()

Note that I wrap all of it into its own function so as to give the whole thing some scope--makes it easier to define in a single .js file and execute.

Scala:

Similarly, Scala allows us to bind functions to names, too:

 def functionAsObject() = { def withdraw(balance : Int, amount : Int) =
{ if (amount <= balance) balance - amount else throw new RuntimeException("Insufficient
funds") } def deposit(balance: Int, amount : Int) = { balance + amount } def
accrueInterest(balance : Int, rate : Float) = { balance + (balance * rate) } }

Again, all of it is wrapped into a function for easier (on me, while I was experimenting with all of this) scoping.

F#:

F#, like most functional/object hybrid languages, also offers the ability to bind functions to values, so this is also pretty straightforward. I choose to just operate against the "global" balance value, rather than do the more functional "pass the balance in" that the previous two use:

let functionAsObject = fun () -> let balance = ref 0 let withdraw =
fun amt -> if amt <= !balance then balance := (!balance) - amt !balance else
raise (Exception("Insufficient funds")) let deposit = fun amt -> balance
:= (!balance) + amt !balance let accrueInterest = fun (intRate : float) -> balance
:= (!balance) + (int (float !balance * intRate)) !balance Console.WriteLine "=========>
Function as Object" printfn "%d" (deposit 200) printfn "%d"
(withdraw 50)

Yeti (ML):

Although Yeti supports a slightly more succinct syntax for defining a function, I choose to use the syntax that more closely matches what we're doing in the other examples--bind a function literal (do ... done;) to a name (withdraw, deposit and accrueInterest). Again, since this is running on top of the JVM, we have full access to the underlying Java library, which means we can make use of RuntimeException again as a cheap way of signaling a bad withdrawal.

withdraw = do bal amt: if amt <= bal then bal - amt else throw new RuntimeException("Insufficient
funds") fi done; deposit = do bal amt: bal + amt done; accrueInterest = do bal
intRate: bal + (bal * intRate) done; balance = 100; println (withdraw balance 10)

Jaskell (Haskell):

C#:

This is a little more verbose than some of the other versions we've seen thus far, because C# lacks the type-inference that F# or Yeti or Scala has, yet requires explicit typing (in some places) because it is a statically-typed language. Again, because the language explicitly forbids the assignment of a lambda/delegate to an implicitly-typed local variable, the local names "withdraw", "deposit", and "accrueInterest" have to be explicitly typed.

static void FunctionAsObject() { var balance = 0; Func<int, int>
withdraw = (amount) => { if (amount <= balance) { balance = balance - amount;
return balance; } else throw new Exception("Insufficient funds"); }; Func<int,
int> deposit = (amount) => { balance += amount; return balance; }; Func<float,
int> accrueInterest = (intRate) => { balance += (int)(intRate * balance); return
balance; }; Console.WriteLine("=============> FunctionAsObject"); Console.WriteLine("{0}",
deposit(100)); Console.WriteLine("{0}", withdraw(10)); }

Notice that again, I choose to operate on the "global" variable "balance", rather than pass it in. (It's fairly easy to imagine how it would look if "balance" were passed in.)

Closure

"You are writing a function with a free variable. How do you bundle a function with a data value defined outside the procedure's body?" If the data value is defined inside the procedure, remember, it gets reset to the same value each time, and obviously this isn't going to track state at all well. "So you might try defining the balance outside the function." But that doesn't work, because now the value isn't encapsulated anymore. "Therefore, create the function in an environment where its free variables are bound to local variables."

Related:
1 2 3 4 5 Page 1
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.