Late-binding with Erlang
This page is a mirrored copy of an article originally posted on the (now sadly defunct) LShift blog; see the archive index here.
Sun, 18 May 2008
Upon browsing the source to the excellent MochiWeb, I came across a call to a function that, when I looked, wasn’t defined anywhere. This, it turns out, was a clue: Erlang has undocumented syntactic support for late-bound method dispatch, i.e. lightweight object-oriented programming!
The following example, myclass.erl
, is a parameterized module, a feature that arrived undocumented in a recent Erlang release. Parameterized modules are explored on the ‘net here and here. (The latter link is to a presentation that also covers an even more experimental module-based inheritance mechanism.)
-module(myclass, [Instvar1, Instvar2]). -export([getInstvar1/0, getInstvar2/0]). getInstvar1() -> Instvar1. getInstvar2() -> Instvar2.
“Instances” of the “class” called myclass
can be created with myclass:new(A, B)
(which is automatically provided by the compiler, and does not appear in the source code), where A
and B
become values for the variables Instvar1
and Instvar2
, which are implicitly scoped across the entirety of the myclass
module body, available to all functions defined within it.
The result of a call to a new
method is a simple tuple, much like a record, with the module name in the first position, and the instance variable values in order following it.
Eshell V5.6 (abort with ^G) 1> Handle = myclass:new(123, 234). {myclass,123,234} 2> Handle:getInstvar1(). 123 3> Handle:getInstvar2(). 234
While this looks really similar to OO dispatch in other languages, it’s actually an extension to Erlang’s regular function call syntax, and works with other variations on that syntax, too:
4> {myclass,123,234}:getInstvar1(). 123
The objects that this system provides are pure-functional objects, which is unusual: many object-oriented languages don’t clearly separate the two orthogonal features of late-binding and mutable state. A well-designed language should let you use one without the other, just as Erlang does here: in Erlang, using parameterized modules for method dispatch doesn’t change the way the usual mechanisms for managing mutable state are used. “Instance variables” of parameterized modules are always immutable, and regular state-threading has to be used to get the effects of mutable state.
I’d like to see this feature promoted to first-class, documented, supported status, and I’d also very much like to see it used to structure the standard library. Unfortunately, it’s not yet very well integrated with existing modules like gb_sets
, ordsets
and sets
. For example, here’s what happens when you try it with a simple lists
call:
5> lists:append([1, 2], [3, 4]). [1,2,3,4] 6> {lists, [1, 2]}:append([3, 4]). [3,4|{lists,[1,2]}]
Not exactly what we were after. (Although it does give brittle insight into the current internals of the rewrites the system performs: a {foo, ...}:bar(zot)
call is translated into foo:bar(zot, {foo, ...})
- that is, the this
parameter is placed last in the argument lists.)
Hi Tony, so you’re playing with Erlang now! Nice to see you “reddite”-ed. :-)