Friday, December 11, 2009

Rosetta Code Submission

I got bored one day, so I wrote up a couple small algorithms from the RosettaCode in D and Erlang. Added Erlang implementations of Run Length Encoding and Merge Sort. Also, I added a second version of Merge Sort for D using the 2.0 version and templates. I didn't add a D version of RLE as my version ended up being similar to the version that was already there.

Hopefully they stay for a while as I'd hate to get credit for someone else's work. The Erlang Merge Sort was fun as I have the shortest implementation on the page! Of course, it helps a lot that the Erlang standard library had both a split and a merge function. However, I did create a multi-process version. I could only try it on a dual-core machine with little improvement. I'd love to know what it could do on a larger box.

Labels: , , , ,

Experiment in OOP

A little background: I was reading an article and they mentioned a very strict definition of Object Oriented Programming and how Erlang is really a OO language because you pass messages to "Objects". In reality, these are processes, but it got me thinking: what is the "true" way to do OOP? I read up on it in Wikipedia and two of the key principles that got me thinking were the message passing between objects and the encapsulation. So, I got to wondering, how could we do this better so that none of the implementation of the class is exposed to the user, but still get relevant data in and out while avoiding getters and setters.

Below is my little experiment written in D:
import std.stdio;
import std.conv;

struct TestMessageData
{
string action;
string status;
string message;
int value;
float floatVal;
}

class Test
{
TestMessageData delegate(TestMessageData)[string] dispatch;

TestMessageData squareValue(TestMessageData msg)
{
msg.value = msg.value * msg.value;
msg.status = "OK";
return msg;
}

TestMessageData toFract(TestMessageData msg)
{
float denominator = to!(float)(msg.value);
if (denominator != 0)
{
msg.floatVal = 1.0 / denominator;
msg.status = "OK";
}
else
{
msg.status = "ERROR";
msg.message = "Cannot divide by 0.";
}
return msg;
}

TestMessageData throwError(TestMessageData msg)
{
throw new Exception("Exception from throwError;");
}

public:

this()
{
dispatch["square"] = &this.squareValue;
dispatch["to_fract"] = &this.toFract;
dispatch["throw_error"] = &this.throwError;
}

TestMessageData sendMessage(TestMessageData msg)
{
debug(test_new_oo)
writeln("Received message with action: ", msg.action);

return this.dispatch[msg.action](msg);
}
}

void main()
{
Test t = new Test();
TestMessageData msg;

msg.value = 3;
msg.action = "square";

auto retval = t.sendMessage(msg);
writeln("Returned with value [", retval.value, "] and status: ", retval.status);

msg.action = "to_fract";
retval = t.sendMessage(msg);
writeln("Returned with value [", retval.floatVal, "] and status: ", retval.status);

TestMessageData msg2;
msg2.value = 0;
msg2.action = "to_fract";
retval = t.sendMessage(msg2);
writeln("Returned with value [", retval.floatVal, "] and status: ", retval.status);

try
{
msg2.action = "throw_error";
retval = t.sendMessage(msg2);
}
catch(Exception e)
{
writeln("Caught: ", e.msg);
}
}
It's not the prettiest, but pretty wasn't a goal. What I was trying out was to send messages in a standardized format, for the class at least, and to hide the implementation details as much as possible. The messages are structs that contain enough data to handle the data we want the object to work upon, what we want the object to do, and the "status" of the result. The status may be the least important, but I put it in as there may be times we get an error that's not considered an exception, therefore we don't want to throw an exception.

As one can see, I performed several operations to show different ways this could work, including errors that do/don't throw exceptions. What I really like about this is that there's only one way to interact with the object, but you still retain the ability to perform a number of operations.

Now, on the problem side, my variable naming sucks, but that's besides the point. While this seems very clean an elegant to me, especially if we want to change out implementations of methods, which in this case means changing a delegate (function pointer) from the old to the new, I'm slightly concerned about performance. The struct takes up some space as well as the dispatch table. Also, is there added overhead from using the dispatch table?

I really don't know. I haven't made up my mind 100% yet, but I felt it should be presented to the world as I think it's at least an interesting exercise. I'm sure there are ways to mitigate some of my concerns. I do have a feeling, though I haven't confirmed it yet, that there are advantages to this type of development. Time to continue pondering...

Labels: ,