public class MoneyTransferContext{
//Declare the destination role
role Destination{
}
//Declare the source role including one role method 'Transfer'
role Source{
//I want to replace self with this to denote the role the method belongs to
//but that will take a bit more work to do.
void Transfer(decimal amount){
self.DecreaseBalance(amount);
self.Log("Decreased balance with " + amount);
Destination.IncreaseBalance(amount);
Destination.Log("Increased balance with " + amount);
}
}
public void Act(){
Source.Transfer(0m);
}
public MoneyTransferContext(dynamic source, dynamic destination){
Destination = destination;
Source = source;
}
}
public class program{
public static void Main(string[] args){
var source = new Account();
var destination = new Account();
new MoneyTransferContext(source,destination).Act();
}
}
public class Account{
public void DecreaseBalance(decimal amount){
Console.WriteLine("Decrease");
}
public void Log(string message){ Console.WriteLine(message);}
public void IncreaseBalance(decimal amount){
Console.WriteLine("Increase");
}
}
Things to note:
The role methods are private to the context and disappears when the object is no longer playing the role
RoleMethods take precedence over instance methods (and over extension methods as well)
It's the plan to support interface based typing of roles as well as structural and dynamic. The syntax will be
contract {
//what ever is legal in a C# interface
}
each role can have one contract section.
Comments are much appreciated
Rune
Hi RuneThat is very cool. I really like that roles come *and* go as needed.My only real comment is about the role assignment. I like that you use the assigment operator - we can it role assignment after all - but maybe it should be the other way around, maybe a more explicit assignment operator than = (because of the time old argument of = being mathematical equality) :source := Sourcedestination := Destinationbecause we say that we assign a role to an object, not the other way around.
Hi Rune,I really like your syntax.There is just one thing that concerns me:When your Roles are implicitly usable as "objects" (or are they?) such as:
role Destination { ... }and then:Destination = destination;and:Destination.IncreaseBalance(amount);
...how would you declare Roles (and instances) in special case, when there is for example:role Teacher {} and role Student {}, but there are an array of Students involved in the use case?
Trygve Reenskaug mailto: try...@ifi.uio.no
Morgedalsvn. 5A http://folk.uio.no/trygver/
N-0378 Oslo Tel: (+47) 22 49 57 27
Norway
Hi Rune,
This is good news indeed. An uncompromising DCI language that is based on a reasonably mainstream language will move DCI towards becoming mainstream.
You have to be careful with your role assignments. Objects cannot be assigned to roles independently, they must be assigned an bloc. This is the only way you can maintain synergy effects; i.e., having a particular configuration of objects with a value exceeding the sum value of its parts. The language should, therefore, not permit the assignment of isolated roles. Role assignments could, for example, be declared in a special language block:
roleAssignmernts begin { role assignment methods }
or in any other way that maintains role cohesion.
...
One possible challenge is that both Jim's Ruby and my DCI/Squeak codes
are recursive. This means that all of the three important roles;
CurrentIntersection, EastNeighbor, and SouthNeighbor; can be mapped to
the same object at the same time. Only one of them is active. The other
two are hidden in the recursion stack, waiting to be re-activated during
the unwinding of the stack.
Another possible challenge is unique to my implementation. Two roles,
EastNeighbor and southNeighbor, are mapped to different instances of the
same class. They both respond to the same message,
recomputeTentativeDistance, while the corresponding role methods are
different. This lead to name collision in the implementation with
injection, but the problem is solved (imperfectly) with my
injection-free compiler.
I have tried to transcribe my injection-free role methods to C#, using
your sample code as a pattern. Do you see a problem here? (Bear with my
faulty code; it is my first attempt at C#)
I look forward to the first alpha of your compiler. I will then
translate my examples to your language.
Cheers
--Trygve
====================
public class BB7CalculateShortestPathCTX
{
//Declare the roles
role EastNeighbor
{
}
role SouthNeighbor
{
}
role DistanceDict
{
}
role CurrentContext
{
}
role Map
{
}
role Unvisited
{
}
role CurrentIntersection
{
void computeDistances
{
//This method computes the final distance for the node named
the CurrentIntersection.
if (EastNeighbor !nil)
{
EastNeighbor.recomputeTentativeDistance
};
if (SouthNeighbor !nil)
{
SouthNeighbor.recomputeTentativeDistance
};
Unvisited.remove (self);
if (Unvisited.!empty)
{
CurrentContext..recur
}
}
}
role EastNeighbor
{
void recomputeTentativeDistance
{
// oldDistance, newDistance; local variables
oldDistance := (DistanceDict [self]).distance.
newDistance := (DistanceDict [CurrentIntersection])
distance + (CurrentIntersection.distanceTo (self));
if (newDistance < oldDistance)
{
DistanceDict.Add(self, (new BB7TentativeDistance
(newDistance , CurrentIntersection)
};
Console.WriteLine("EastNeighbor");
}
}
role SouthNeighbor
{
void recomputeTentativeDistance
{
// oldDistance, newDistance; local variables
oldDistance := (DistanceDict [self]).distance.
newDistance := (DistanceDict [CurrentIntersection])
distance + (CurrentIntersection.distanceTo (self));
if (newDistance < oldDistance)
{
DistanceDict.Add(self, (new BB7TentativeDistance
(newDistance , CurrentIntersection)
};
Console.WriteLine("SouthNeighbor");
}
}
public BB7CalculateShortestPathCTX (dynamic geometry, dynamic
originName, dynamic destinationName)
{
.....
}
}
Hi Rune,
Re: the code you posted on 2011.11.03 01:34. Some of my DCI/Squeak examples may pose special challenges to your compiler. The Dijkstra example is one of them.
One possible challenge is that both Jim's Ruby and my DCI/Squeak codes are recursive. This means that all of the three important roles; CurrentIntersection, EastNeighbor, and SouthNeighbor; can be mapped to the same object at the same time. Only one of them is active. The other two are hidden in the recursion stack, waiting to be re-activated during the unwinding of the stack.
Another possible challenge is unique to my implementation. Two roles, EastNeighbor and southNeighbor, are mapped to different instances of the same class. They both respond to the same message, recomputeTentativeDistance, while the corresponding role methods are different. This lead to name collision in the implementation with injection, but the problem is solved (imperfectly) with my injection-free compiler.