Tuesday, August 22, 2006

New: Module Extension in Erlang with Smerl

Have you ever wished you could easily add all exports from a "parent" module to a "child" module in the form of remote function calls to the parent, without overwriting the child module's exported functions?



If you have, today is your lucky day! With Smerl, you can now do it in a single line of code by calling smerl:extend/2 :)



I know words like 'extension' and 'inheritance' are generatlly shunned in the functional programming world, and for a good reason: in OO languages, these concepts have caused endless complexity, crufty code and bug-ridden horrors.



However, I should stress that the Smerl extension mechanism doesn't violate any functional programming tenets. Smerl's extend/2 is basically a smart function generator. It does not allow you to extend data fields in any way, nor does it create any artifical bindings between data and code.



(I believe Haskell has a similar, statically typed approach to module inheritance, but I only have rudimentary knowledge of Haskell so I may be wrong on that one.)



Here's a quick example:



P1 = smerl:new(parent),
{ok, P2} = smerl:add_func(P1, "add(A, B) -> A+B."),

%% 'false' indicates to not export the function
{ok, P3} = smerl:add_func(P2, "subtract(A, B) -> A-B.", false),

{ok, P4} = smerl:add_func(P3, "multiply(A,B) -> A*B."),
smerl:compile(P4),

C1 = smerl:new(child),
{ok, C2} = smerl:add_func(C1,
"multiply(A,B) -> throw(not_allowed)."),

C3 = smerl:extend(P4, C2),
smerl:compile(C3),

8 = parent:add(3,5),
{'EXIT', {undef, _}} = begin catch parent:subtract(4,3) end,
6 = parent:multiply(3, 2),
11 = child:add(2,9),
not_allowed = begin catch child:multiply(5,3) end,
{'EXIT', {undef, _}} = begin catch child:subtract(4,2) end.


Seems exotic? Maybe. However, I think this feature is very powerful. I have actually seriously needed this feature in my own project. If you have needed it too, I hope I just made your life a bit easier :)



Please let me know if you have any problems or suggestions.



Enjoy.



Update (8/23/06): The extend/2 function now also embeds the parent/0 function in the child module. parent/0 returns an atom with the name of the parent's module. This allows child functions to explicitly invoke parent functions (among other things).

6 comments:

Nick Thomas said...

Oops, sorry for the double post. :/

Nick Thomas said...

Just FYI: Haskell doesn't have any form of module inheritance. (At least not in the standard; anything is possible with all of the different hacks on GHC that are out there.) Perhaps you're thinking of type classes?

Nick Thomas said...

Just FYI: Haskell doesn't have any form of module inheritance. (At least not in the standard; anything is possible with all of the different hacks on GHC that are out there.) Perhaps you're thinking of type classes?

Yariv said...

Yes, I guess I had type classes in mind. I did a Haskell tutorial a while ago and I vaguely remembered this kind of "inheritance" but I didn't exactly know if it's the same as Smerl's module extension and I was too lazy to go read the documentation :)

Brandon said...

The Haskell module system has syntax for re-exporting all the things imported from another module, and for importing all but a few things from another module. It's close to your extension operation, except you have to explicitly hide the things you want to replace. Details of the Haskell module system here: http://haskell.org/onlinereport/modules.html. What happens in your system if you extend (extrude?) several parents into a module?

Yariv said...

Brandon, thanks for the pointer. In my system, you can't really extrude several parents into the same module, but you can interatively extrude each parent into the result from the last extrusion. Then end result would be a module that includes the union of the child's functions plus all functions from the parents, where if a function has multiple "instances" across modules, only the first one is picked.

The Smerl module extension is currenly useful for extension in runtime (that's what I needed it for) but it wouldn't be hard to spin it off as a compile-time script, which would effectively make an Erlang language extension (there would have to be a new convention for denoting the extension relations in compile-time, of course).