Objective-D, reflective programming, dynamic typing
Eljay
eljay at adobe.com
Thu Apr 2 08:20:13 PDT 2009
/*
Although D 2.0 is a multi-paradigm programming language, it does not support
two paradigms:
+ reflective programming
+ dynamic typing
Since D 2.0 does not have support for reflective programming, nor support for
dynamic typing, I thought "What facilities does D 2.0 have which could be used
to mimic reflective programming and dynamic typing?"
(Unless I'm mistaken, and D 2.0 does support these paradigms. Andrei and Walter
have been very busy! In which case... oops.)
This toy program is the result.
This toy program does not try to achieve efficient performance.
Id - root class for all dynamic types.
Msg - a string:string associative array.
TODO. Need to change to string:Id dictionary, but that's for later.
TODO. Need to have Id wrapper for string, and other primitives.
Sel - the selector, which is the "@" key bound to the selector string in the
string dictionary.
What is lacking?
- Should be able to add message responders to an object.
What does this mean?
Create an object.
Insert a message responder.
Send that message, and notice that the object now performs that message.
Only affect that instantiated object, not the whole class.
- Should be able to add message responders to a class.
What does this mean?
If someone has a black box class called MyString, you should be able to inject
a brand new message responder into that class which affects ALL instances of
MyString.
Upshot: you can inject new message handlers to an existing class, which is not
a class to which you have the source.
- Should be able to override message responders to a class, and inject your own
message responder.
What does this mean?
Let's say you want to do some "first grade debugging" on a class, you should
be able to programmatically get the message responder of a class (even a class
to which you do not have the source),
save that message responder, inject your own diagnostic message responder into
a class (or into a single instantiated object), output the diagnostics than
invoke the original message responder.
- Classes themsleves should be singleton (or monostate) objects, which are
object instantiation factories.
Objective-C designates class message responders and object message responders
by (note the leading '+' and '-'):
+ (void)IAmAClassMessageResponder(void)
- (void)IAmAnObjectMessageReponder(void)
Just as a programmer can do object-oriented programming in pure C, even though
the C programming language provides no language support for object-oriented
programming, so too can you do reflective programming in D. Even though D does
not provide any dynamic typing and reflecting programming support in the D core
language.
The only thing that is required to do reflective programming and have dynamic
typing in D is superhuman discipline, and adhering meticulously to a rigid
programming convention and never ever make a mistake.
*/
import std.stdio;
import std.conv;
alias string[string] Msg;
/*
** This is the ultimate root of all reflective objects.
** Wouldn't it be nice if this were class object?
*/
class Id
{
void perform(in Msg msg)
{
switch(msg["@"])
{
default:
{
writeln("Unable to perform \"", msg["@"], "\"");
}
break;
}
}
};
/*
** This is an example of a useful object which does something interesting.
*/
class IdFoo : Id
{
void perform(in Msg msg)
{
switch(msg["@"])
{
case "add":
{
performAdd(msg);
}
break;
case "sub":
{
performSub(msg);
}
break;
case "mul":
{
performMul(msg);
}
break;
case "div":
{
performDiv(msg);
}
break;
default:
{
super.perform(msg);
}
break;
}
}
void performAdd(in Msg msg)
{
double a = toDouble(msg["a"]);
double b = toDouble(msg["b"]);
writeln(a, " + ", b, " = ", (a + b));
}
void performSub(in Msg msg)
{
double a = toDouble(msg["a"]);
double b = toDouble(msg["b"]);
writeln(a, " - ", b, " = ", (a - b));
}
void performMul(in Msg msg)
{
double a = toDouble(msg["a"]);
double b = toDouble(msg["b"]);
writeln(a, " * ", b, " = ", (a * b));
}
void performDiv(in Msg msg)
{
double a = toDouble(msg["a"]);
double b = toDouble(msg["b"]);
writeln(a, " / ", b, " = ", (a / b));
}
}
/*
** This is an example of a null object.
** Imagine that the null object is a noisy debugging object, used to substitute
** in for an object which has been destroyed.
*/
class IdNull : Id
{
void perform(in Msg msg)
{
switch(msg["@"])
{
default:
{
performDiscard(msg);
}
break;
}
}
void performDiscard(in Msg msg)
{
writeln("Discard ", msg["@"]);
}
}
/*
** This is an example of a proxy object, which forwards message to an object
** elsewhere.
**
** Imagine that the other object exists on another computer, and this forwarding
** object sends messages to the remote object over an IP/TCP connection.
*/
class IdForward : Id
{
void perform(in Msg msg)
{
switch(msg["@"])
{
default:
{
performForward(msg);
}
break;
}
}
void performForward(in Msg msg)
{
writeln("Proxy object forwarding message ", msg["@"]);
forwardObj.perform(msg);
}
this(Id obj)
{
forwardObj = obj;
}
Id forwardObj;
}
void Exercise(Id obj)
{
Msg msg;
// [obj addWithA:5 withB:3] -- Objective-C
// obj(add a:5 b:3) -- Pseudo-D ... perhaps?
// obj(add a:"5" b:"3") closer to what we are actually doing
// but not what's ultimately desired.
msg["@"] = "add"; // Just using "add", but could have been "addWithA:withB:"
msg["a"] = "5";
msg["b"] = "3";
obj.perform(msg);
// [obj subWithA:5 withB:3]
// obj(sub a:5 b:3)
msg["@"] = "sub";
obj.perform(msg);
// [obj mulWithA:5 withB:3]
// obj(mul a:5 b:3)
msg["@"] = "mul";
obj.perform(msg);
// [obj divWithA:5 withB:3]
// obj(div a:5 b:3)
msg["@"] = "div";
obj.perform(msg);
// [obj fooWithA:5 withB:3]
// obj(foo a:5 b:3)
msg["@"] = "foo";
obj.perform(msg);
}
void main()
{
Id obj = new IdFoo;
writeln(">>> Exercise IdFoo");
Exercise(obj);
obj = new IdNull;
writeln("\n>>> Exercise IdNull");
Exercise(obj);
obj = new IdForward(new IdFoo);
writeln("\n>>> Exercise IdForward(IdFoo)");
Exercise(obj);
writeln("---done---");
}
More information about the Digitalmars-d
mailing list