Looking for some feedback on my DCI experience so far...

135 views
Skip to first unread message

Harris Rodgers

unread,
Feb 27, 2014, 9:42:03 PM2/27/14
to object-co...@googlegroups.com

I’m working on a new cordova application that uses node for the server side. I have read about DCI in the past found it very exciting. Javascript seems like a great language for DCI so I decided to give it a try. My company uses scrum and it’s been easy to translate from user story to tests (jasmine) to DCI but I have encountered a few situations that I’m not sure how to handle. 

I made two examples that give the basic idea of my approach and demonstrate one of my concerns. When you look at the code, please don’t pay too much attention to the AssignRole or RemoveRole methods. In my “real” application I use better js practices and some other wire-up code to avoid having to call assign/remove in every execute. I wanted the samples to be a simple version so I only included what seemed necessary to communicate my direction.

Please review the tests first since they include the user story as a comment on line 1 and they describe the use case.

Code is there -> https://github.com/harrisrodgers/dcisample

In Sample 1...

1.) Does the overall approach appear to be on the right track?

In Sample 2...

1.) The validation messages part doesn’t seem right. Is there a more common approach for use cases that are basically “creating” data?

2.) Does it make sense to declare and assign methodless roles or do you think the parameter name is enough for future developers to understand?

Any other comments would definitely be helpful as well!

Egon Elbre

unread,
Feb 28, 2014, 4:38:55 AM2/28/14
to object-co...@googlegroups.com
I would write something like: https://gist.github.com/egonelbre/9268118 ... whether it's a better approach, I don't know.

(Also what's the story with the "every other line is empty"? It makes the code much harder to read, and also unnecessary, since if needed line-spacing can be usually be adjusted in the editor.)

+ egon

Andreas Söderlund

unread,
Feb 28, 2014, 11:18:45 AM2/28/14
to object-co...@googlegroups.com
Hello Harris, nice to see some DCI/javascript activity! DCI in javascript is tricky because of the injection-based solution that seems simple, but has a couple of pitfalls. So I'll make this a two-part post, first the feedback that you asked for, then I'll elaborate on eventual problems with DCI in pure js.

You talk about user stories in the beginning, not use cases. Is the mental model described in a use case somewhere? I haven't given any thought about using BDD-style requirements to map to a DCI Context, but it doesn't look like a great match currently when I compare the tests with the Context. Maybe it can be improved, but I suspect that mapping to should-statements would make a strange looking Context. For example, it looks like every requirement have plenty (too many?) Role-candidates. Interesting area though! Thoughts, anyone else?

In Sample 1 there seems to be some Role name confusion. Inside a Context the Roles should be easily accessible by name, but there are both Role-suffixes (attendeeRole) and parameters that refer to a Role but with a different name ("event", in attendEvent). The best would be if you had "attendee" and "eventBeingAttended" accessible within the Context, as shown by Egon in his examples, to get rid of "event" and "attendee" as parameters and duplicate Role-references. (Remember that a Role is only an identifier.)

Except for that, the approach seems good. You're calling a RoleMethod directly from the execute function and using the distributed algorithm for interaction, as it should be.

Same Role-binding issue with Sample 2, but except for that it looks fine. It's again about mapping Roles and their RoleMethods to a mental model. If an "eventToSchedule" is mentioned in requirements, between people, in the domain, and it's supposed to be "validated", then it's a match. Same goes for "eventOrganizer" if it "schedules events".

To answer question 2, you should definitely specify Roles with no RoleMethods, since they will be bound and referred to in the Context anyway. For example "amount" in the money transfer example.

That concludes the comments on your specific code, now to the deep, dark stuff... :)

I've been doing quite a bit of js/DCI-experimenting with my coffeescript library: https://github.com/ciscoheat/coffee-dci

Being an injection-style solution, I had the idea of pushing the affected object properties on a stack, replaced them with RoleMethods and popped them back after the interaction was completed. (I assume you're doing something similar or throwing an exception if there are property name conflicts when binding Roles.) It worked fine with simple "imperative-style" code, but then, enter callbacks and closures...

A callback for example sends the execution outside the Context, from inside a RoleMethod. Now how to unbind the RoleMethods, since they cannot exist outside their Context? Track every jump outside the current Context object, but how? Doing it manually is simply a too large burden for the programmer, and also results in ugly code. A similar problem exists with closures. This was my showstopper for injection-based DCI.

Multiple role bindings is another problem, possibly easier to alleviate but I don't think something like this can be done with injection-based DCI without jumping through plenty of hoops: http://fulloo.info/doku.php?id=it_seems_like_many_implementations_are_using_abstract_syntax_tree_rewriting_to_enable_dci._how_does_it_work

So we're basically down to AST-rewriting, which Egon has a straightforward JS example for: String substitution. It also works in simpler code, but similarly, continuations can give a lot of headache. For example, putting this code after line 16 on the example at http://egonelbre.com/js/dci/ will break simple srep: (below the "Current = Initial" line)

var c = Current;
var d = Current.markVisited;
var Path = {to: function() { alert("to"); }};

c.markVisited();
d();
Path.to();

The last example, replacing a Role with a local var is perhaps a bit extreme but I think it generally shows that some lexical analysis is required for a foolproof implementation in today's popular languages. I don't mean to bash on your library Egon, I'm just applying the problems I had and realized that they are hard to resolve.

This became a long one, but I wanted to get some "closure" on my endeavors with DCI in javascript, and give a heads-up that it may not be as simple as it looks. My vision is to use the rewritten coffeescript compiler to create a fully working DCI implementation. Haven't had the time or fortitude to do that yet, though...

As always, feedback and critique welcome.


/Andreas


--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
To post to this group, send email to object-co...@googlegroups.com.
Visit this group at http://groups.google.com/group/object-composition.
For more options, visit https://groups.google.com/groups/opt_out.

Egon Elbre

unread,
Feb 28, 2014, 11:36:01 AM2/28/14
to object-co...@googlegroups.com


On Friday, February 28, 2014 6:18:45 PM UTC+2, Andreas Söderlund wrote:
Hello Harris, nice to see some DCI/javascript activity! DCI in javascript is tricky because of the injection-based solution that seems simple, but has a couple of pitfalls. So I'll make this a two-part post, first the feedback that you asked for, then I'll elaborate on eventual problems with DCI in pure js.

You talk about user stories in the beginning, not use cases. Is the mental model described in a use case somewhere? I haven't given any thought about using BDD-style requirements to map to a DCI Context, but it doesn't look like a great match currently when I compare the tests with the Context. Maybe it can be improved, but I suspect that mapping to should-statements would make a strange looking Context. For example, it looks like every requirement have plenty (too many?) Role-candidates. Interesting area though! Thoughts, anyone else?

In Sample 1 there seems to be some Role name confusion. Inside a Context the Roles should be easily accessible by name, but there are both Role-suffixes (attendeeRole) and parameters that refer to a Role but with a different name ("event", in attendEvent). The best would be if you had "attendee" and "eventBeingAttended" accessible within the Context, as shown by Egon in his examples, to get rid of "event" and "attendee" as parameters and duplicate Role-references. (Remember that a Role is only an identifier.)

Except for that, the approach seems good. You're calling a RoleMethod directly from the execute function and using the distributed algorithm for interaction, as it should be.

Same Role-binding issue with Sample 2, but except for that it looks fine. It's again about mapping Roles and their RoleMethods to a mental model. If an "eventToSchedule" is mentioned in requirements, between people, in the domain, and it's supposed to be "validated", then it's a match. Same goes for "eventOrganizer" if it "schedules events".

To answer question 2, you should definitely specify Roles with no RoleMethods, since they will be bound and referred to in the Context anyway. For example "amount" in the money transfer example.

That concludes the comments on your specific code, now to the deep, dark stuff... :)

I've been doing quite a bit of js/DCI-experimenting with my coffeescript library: https://github.com/ciscoheat/coffee-dci

Being an injection-style solution, I had the idea of pushing the affected object properties on a stack, replaced them with RoleMethods and popped them back after the interaction was completed. (I assume you're doing something similar or throwing an exception if there are property name conflicts when binding Roles.) It worked fine with simple "imperative-style" code, but then, enter callbacks and closures...

A callback for example sends the execution outside the Context, from inside a RoleMethod. Now how to unbind the RoleMethods, since they cannot exist outside their Context? Track every jump outside the current Context object, but how? Doing it manually is simply a too large burden for the programmer, and also results in ugly code. A similar problem exists with closures. This was my showstopper for injection-based DCI.

Multiple role bindings is another problem, possibly easier to alleviate but I don't think something like this can be done with injection-based DCI without jumping through plenty of hoops: http://fulloo.info/doku.php?id=it_seems_like_many_implementations_are_using_abstract_syntax_tree_rewriting_to_enable_dci._how_does_it_work

So we're basically down to AST-rewriting, which Egon has a straightforward JS example for: String substitution. It also works in simpler code, but similarly, continuations can give a lot of headache. For example, putting this code after line 16 on the example at http://egonelbre.com/js/dci/ will break simple srep: (below the "Current = Initial" line)

var c = Current;
var d = Current.markVisited;
var Path = {to: function() { alert("to"); }};

c.markVisited();
d();
Path.to();

The last example, replacing a Role with a local var is perhaps a bit extreme but I think it generally shows that some lexical analysis is required for a foolproof implementation in today's popular languages. I don't mean to bash on your library Egon, I'm just applying the problems I had and realized that they are hard to resolve.

Nah... I'm ok with bashing :)...

The http://egonelbre.com/js/dci/ solution is a proof-of-concept, and it doesn't try to do any complex analysis during the transformation part... basically it's not a AST transformation it's a source transformation.

Regarding your breaking example, the behavior of that code should be correct from my understanding.

Role methods should be only resolved through direct invocation on Role, hence...

var c = Current; // this should bind the object to c and remove any role information, since "c" isn't playing the role "Current".
var d = Current.markVisited; // this should set "d" to the object property markVisited, since "Current.markVisited" is a role method, and assigning it as a value would violate the "roles having data" property.

Path.to(); // this should call the role method instead of the object method since you are invocating via Role name.

The actual breaking behavior can be seen by adding line:

var c = "Current.markVisited()";
 
Of course there are cases with nested contexts as well...

+ egon

Andreas Söderlund

unread,
Feb 28, 2014, 12:24:34 PM2/28/14
to object-co...@googlegroups.com
2014-02-28 17:36 GMT+01:00 Egon Elbre <egon...@gmail.com>:

Nah... I'm ok with bashing :)...

You always have solid argumentation, so I can understand that. :)
 
The http://egonelbre.com/js/dci/ solution is a proof-of-concept, and it doesn't try to do any complex analysis during the transformation part... basically it's not a AST transformation it's a source transformation.

Regarding your breaking example, the behavior of that code should be correct from my understanding.

Role methods should be only resolved through direct invocation on Role, hence...

I haven't seen or heard that before, do you have a reference? Couldn't find it in the DCI execution model either. (http://fulloo.info/Documents/DCIExecutionModel-2.1.pdf)

I don't see why a local variable assignment should change the semantics; it seems quite confusing. It reminds me of yet another problem I had with injection-DCI, deferred execution:

function execute(some, roles) {
    bind(some, roles); // Manual binding to illustrate
    oneRole.doStuff().then(anotherRole.doOtherStuff); // doStuff immediately returns a promise that should continue later with a RoleMethod.
    unbind(some, roles); // Objects are unbound immediately, before the deferred execution. Oops!
}

This is a common pattern which will break according to "direct invocation only", so I'd like to know more about its grounds.

The actual breaking behavior can be seen by adding line:

var c = "Current.markVisited()";

Ah yes, good find/bash. :)

James O Coplien

unread,
Feb 28, 2014, 12:35:45 PM2/28/14
to object-co...@googlegroups.com
Hi, Harris,

Thanks for your courage in diving in. Here are my very quickly considered thoughts — from perhaps a strange perspective.

On 28 Feb 2014, at 03:42, Harris Rodgers <harris....@gmail.com> wrote:

I’m working on a new cordova application that uses node for the server side. I have read about DCI in the past found it very exciting. Javascript seems like a great language for DCI so I decided to give it a try. My company uses scrum and it’s been easy to translate from user story to tests (jasmine) to DCI but I have encountered a few situations that I’m not sure how to handle. 

I'm not sure why you mention Scrum. There are no user stories in Scrum. There is no translation of user stories to tests in Scrum.

We don't talk about translating user stories to tests in DCI.

I mention these facts as prelude to putting forth what I feel would help me better understand your code. Good Scrum projects start with as much as six months to five years of analysis (source: Jeff Sutherland); in this case, it would be a good domain analysis that helps you decide your class structure. I hear you talking about the user story part but the design of each of those is usually done during backlog grooming and elaborated during a Sprint; that's the small stuff. Maybe you use user stories to demarcate planned deliverables on your product backlog, but a user story never has enough context (and this concept is key in DCI) to be able to implement it. The big stuff is figuring out the domain picture, and that figures heavily in Product Owner activities.

So I'd like to see your domain model. It would also be good to see the list of Sprint Goals you use to delineate the PBIs on your Product Backlog; from that it would be interesting to see the mapping onto DCI Contexts. For my money, one of the goals of DCI is to make Contexts so understandable that you don't need to write a user story. You sit down with a client and you start coding, perhaps pair programming.

Given that, it's no wonder that you end in situations where you find yourself stuck.

If you're looking for tool support I suggest you use use cases, as DCI advocates. A lot of the analysis behind DCI is about consolidating your role model. If you haven't done role consolidation, you aren't going to succeed at DCI. If you don't have roles, you can't consolidate them. And if you have user stories, it's unlikely that you have a good role taxonomy at all.

Your code in Sample 1 looks like simple procedural decomposition. There is one role method per role, which suggests that roles don't have much expressive power. I think a simple procedural version would be much more readable — and readability is the goal here.

In Sample 2, you pass an argument to the validate method of the eventToSchedule role. I do that occasionally but, the more I use DCI, the more I am inclined to make things like validationMessages a Context identifier (a methodless role in its own right).

Harris Rodgers

unread,
Feb 28, 2014, 1:40:16 PM2/28/14
to object-co...@googlegroups.com
Thanks for the response! I definitely like the usage of your approach. I see how it solves my role naming/duplication problem pointed out by Andreas. I'm still digesting your code for wiring it all up (Context and ContextMacro) but I see you've handled things like naming collisions which is good. I was planning to implement something similar in my "real" application. It's funny you mention the spacing. That's just a convention that my team follows. I did it out of habit without realizing others might find it strange haha!

Harris Rodgers

unread,
Feb 28, 2014, 2:14:33 PM2/28/14
to object-co...@googlegroups.com
Andreas, great response with lots of good information, thanks!

Maybe some background will help explain where I'm coming from on the user story/use case bdd test subject. I work as a developer on a team with a product owner, two user experience designer/developers and one other software developer. Every two weeks we set a goal, discuss and select some user stories from the backlog and plan a two week sprint. The backlog is primarily written by the product owner, but anyone on the team is empowered to contribute to it.

Our general process for working on a user story starts at the whiteboard. We spend a day or two (depending on scope) finding a solution and fitting it into the rest of the application. If we can, we go directly from whiteboard to code, but sometimes the UX designers will make higher fidelity mock-ups to help communicate our intended solutions to stakeholders off of the team. Throughout or immediately after each conversation, I try to capture the primary business logic in the form of tests. The tests normally describe a use of the application (not a traditional use case) and the data/roles/interactions that will be required. After reading about DCI it seemed like it would be an architectural pattern that would help us have better flexibility for this approach to problem solving. So far my very limited version of a dci framework which is a little more 'fully baked' than what I shared earlier has helped us move and refactor faster. 

Sorry for the long winded explanation but that is basically how I got here from user stories. :)

Moving on, I see your point about role name confusion and I agree that something like what Egon is doing would fix that right up. I was definitely unclear about whether to name the parameters to my execute method for the data or the role. In fact I also wonder, when looking at a context, should I see/know/care what data plays a role? I guess this question is related to your question about having more/too many role candidates. Would you mind talking about that subject a little more?

With regard to your concerns about callbacks and promises. I looked at your coffeescript project and I see what you mean. Since I'm working with a relatively small team I guess for the time being I will have to make it known that I don't currently support them. I think even without this style of thinking/coding has great advantages. I did ask my supervisor if I could share some of my actual code, at least the framework code with the group but I'm not sure when or if that will be allowed. Unfortunately that left me coming up with a 'made up' example which will have flaws because it's not a real product.

Thanks again for all of your suggestions and comments!

Egon Elbre

unread,
Feb 28, 2014, 3:23:11 PM2/28/14
to object-co...@googlegroups.com


On Friday, February 28, 2014 7:24:34 PM UTC+2, Andreas Söderlund wrote:
2014-02-28 17:36 GMT+01:00 Egon Elbre <egon...@gmail.com>:

Nah... I'm ok with bashing :)...

You always have solid argumentation, so I can understand that. :)
 
The http://egonelbre.com/js/dci/ solution is a proof-of-concept, and it doesn't try to do any complex analysis during the transformation part... basically it's not a AST transformation it's a source transformation.

Regarding your breaking example, the behavior of that code should be correct from my understanding.

Role methods should be only resolved through direct invocation on Role, hence...

I haven't seen or heard that before, do you have a reference? Couldn't find it in the DCI execution model either. (http://fulloo.info/Documents/DCIExecutionModel-2.1.pdf)

Imagine a single Player playing two roles Attacker, Defender... now both have some method named "counter". Let's say you pass the "Attacker" into some function "invokeCounter(Attacker)" (sorry, couldn't think of a better function for that)... where the function is just "function invokeCounter(whom){  whom.counter(); }".. how would you know which method to invoke... the "Attacker.counter" or "Defender.counter"? Essentially there are three possibilities here... either introduce a wrapper, which really doesn't work due to object schizophrenia. Or try specialize the function for "Attacker"... which may work, but I highly doubt it because it seems to be like something that has a lot of corner cases... e.g. recursive calls, comparison with the roles etc. Or have a activation context kind of thing that tracks the role information in some meta structure. But the simplest way is to say... it's a Role only if it's explicitly said so, it also makes the code more concrete as well, helping with the DCI understandability. But that's just my reasons...

 

I don't see why a local variable assignment should change the semantics; it seems quite confusing. It reminds me of yet another problem I had with injection-DCI, deferred execution:

This is somewhat just a peculiarity of dci.js... basically it would require a proper parser to make it better. Currently when you declare a role "Path" then you can't have any variable with name "Path"... in essence it's only for referencing the role... assignment to the variable means binding a value to the role. So ideally it wouldn't allow doing "var Path".

Path = {...};

Means bind the {...} thing to the role Path.

Andreas Söderlund

unread,
Feb 28, 2014, 7:23:21 PM2/28/14
to object-co...@googlegroups.com
2014-02-28 21:23 GMT+01:00 Egon Elbre <egon...@gmail.com>:

Imagine a single Player playing two roles Attacker, Defender... now both have some method named "counter". Let's say you pass the "Attacker" into some function "invokeCounter(Attacker)" (sorry, couldn't think of a better function for that)... where the function is just "function invokeCounter(whom){  whom.counter(); }".. how would you know which method to invoke... the "Attacker.counter" or "Defender.counter"?

I see that example more like the one I mentioned in my first response to Harris, a Role-name confusion. "whom" being defined in a RoleMethod signature and referring to a Role, I agree that's ambiguous, but not just when compiling. It also seems like a problem with the underlying mental model.

Compare this to my example. I think I'm pretty explicit when saying:

var d = Current.markVisited; // "I want d to refer to a certain RoleMethod (for later usage)."

And this statement could be easily rewritten to "var d = Current$markVisited", since it's an explicit RoleMethod reference, not just immediately executed. If the goal is understandable code, referring to a RoleMethod inside a Context must make more sense than wanting to reference to a most likely non-existing object property outside the Context, breaking patterns like futures/promises, don't you think?

I understand it's possible to write and execute the invokeCounter example, and I don't really know what to do about it in javascript. As Trygve said in the post you linked to, "The programmer presumably knows what he is doing; the compiler can't guess what it is." Maybe we have to keep it like that in those edge cases?

/Andreas

Andreas Söderlund

unread,
Feb 28, 2014, 8:16:47 PM2/28/14
to object-co...@googlegroups.com
2014-02-28 20:14 GMT+01:00 Harris Rodgers <harris....@gmail.com>:
Andreas, great response with lots of good information, thanks!

No problem, glad I could provide some assistance. Thanks for your background explanation as well, I think the answer from Cope is worth its weight in gold in that area. He knows his stuff...!
 
Moving on, I see your point about role name confusion and I agree that something like what Egon is doing would fix that right up. I was definitely unclear about whether to name the parameters to my execute method for the data or the role. In fact I also wonder, when looking at a context, should I see/know/care what data plays a role? I guess this question is related to your question about having more/too many role candidates. Would you mind talking about that subject a little more?

What's important to know about the (data) object playing a Role, is if it satisfies the RoleInterface. That's the actual link between the stateless RoleMethods and the underlying data. RoleMethods specifies the interaction between objects, but nothing really happens until you call something defined in a RoleInterface.

In javascript the RoleInterface can be implicit, or you can have some basic runtime checking, like here: https://github.com/ciscoheat/coffee-dci/blob/master/tests/spec/context.coffee#L10 (should really be called "interface" or similar, not "contract")

Or if you're using a statically typed language with great type inference, you can be very explicit and get an error at compile-time if a type doesn't implement the RoleInterface: https://github.com/ciscoheat/haxedci-example/blob/master/src/dci/examples/moneytransfer/contexts/MoneyTransfer.hx#L32

When I talked about too many Role candidates, I originally saw many possible Role references in the requirements, but having looked and thought about it some more, it's rather a mix of Roles and Data, and the "it"-style gives it an imperative tone, which isn't very good for defining object collaboration (more info in the FAQ). So I'm inclined to say that building Contexts from BDD-requirements isn't the best approach. Of course, Cope said it already in his reply... :)

With regard to your concerns about callbacks and promises. I looked at your coffeescript project and I see what you mean. Since I'm working with a relatively small team I guess for the time being I will have to make it known that I don't currently support them. I think even without this style of thinking/coding has great advantages. I did ask my supervisor if I could share some of my actual code, at least the framework code with the group but I'm not sure when or if that will be allowed. Unfortunately that left me coming up with a 'made up' example which will have flaws because it's not a real product.

Great to hear that your team is open minded and wants to try out these ideas! As Cope also said, a solid domain model is very important, so if you have that the DCI principles will be much easier to apply.

/Andreas

Matthew Browne

unread,
Mar 1, 2014, 12:30:16 AM3/1/14
to object-co...@googlegroups.com
On Friday, February 28, 2014 12:35:45 PM UTC-5, Cope wrote:

In Sample 2, you pass an argument to the validate method of the eventToSchedule role. I do that occasionally but, the more I use DCI, the more I am inclined to make things like validationMessages a Context identifier (a methodless role in its own right).

+1
As I've worked with DCI more, I've come to appreciate the value of declaring roles even if they have no methods, which might not be obvious at first. Overall I like Egon's versions of your samples, but I think that for the Errors role, you could just as easily use Javascript's native push() method to add errors to the array as you did in your original example -- the Errors.add() role method is perhaps redundant. In your original code you have:

var validationMessagesRole = { }; // no methods so not explicitly needed?

Personally I would leave that bit of the code as is...although there are no methods, it's still valuable to have all your roles clearly identified. One of the major benefits of DCI is that you can look at the code at a glance and immediately have an overview of what kinds of objects are involved in a given context and how they will interact with each other. ValidationMessages certainly qualifies as a role, and so it should be declared as such, just as Amount is a role in the Transfer Money context (even though it has no role methods).

As I was thinking about this, I had a question of my own: what kind of objects in a context can be considered to not be role players? Contexts can be stateful, and so can contain any number of data properties...I think perhaps the best criteria would be whether or not that object participates in an interaction with other role players, but then you'd need to define more precisely what "participates in an interaction" means...I'm having a bit of a hard time articulating this but if anyone knows what I'm getting at, please share your thoughts. In any case, ValidationMessages is a pretty clear example of a role with no methods that should be declared as such.

Side note: In the Lean Architecture book, "methodless roles" is a term used to refer to what we now refer to as role contracts. Role contracts are interfaces that specify what behavior the data objects playing a given role need to support, in order to be able to play the role. When using role contracts, each role has two declarations: one for the role contract, and one for the role itself. Above I used the phrase "role with no methods", to clarify that I'm not talking about a role contract, but rather a role with no role methods, e.g. your ValidationMessages role.

Matthew Browne

unread,
Mar 1, 2014, 12:47:29 AM3/1/14
to object-co...@googlegroups.com
With regard to method injection vs. injectionless DCI in Javascript, I also prefer the injectionless style. Although you can do a pretty nice DCI implementation in pure Javascript, I think the little bit of source code transformation needed to make injectionless DCI work is well worth it. Thanks Andreas for describing why.

I am happy to see growing interest in DCI in Javascript and particularly node.js, which I am also a fan of. Egon's implementation is perhaps the simplest currently-available injectionless DCI implementation for Javascript because it's all pure Javascript and doesn't involve any other languages. I haven't checked out Andreas's CoffeeScript implementation yet but I'm glad to hear that's progressing as well.

I too am working on an injectionless DCI implementation for Javascript. I haven't posted the link here until now because I'm still working on making it easier to get started using it for client-side JS, but it's pretty straightforward if you're using node.js as you are. I achieved the source transformation by using the TypeScript compiler; you can download the library here:

https://github.com/mbrowne/typescript-dci/

Currently only plain Javascript syntax is supported but I have plans to support some of the other features made possible by TypeScript in the future, such as classes (which are based on the ECMAScript 6 class proposal) and interfaces (which should make it possible to implement role contracts).

Here's an example of the source code it makes possible, which is quite similar to Marvin (except that roles are bound with the special <- role-binding operator rather than the assignment = operator):

/**
 * Transfer Money use case
 */
function TransferMoney(sourceAcct, destinationAcct, amount) {

    //bind the objects to their roles
    SourceAccount <- sourceAcct;
    DestinationAccount <- destinationAcct;
    Amount <- amount;
   
    role SourceAccount {
       
        //transfer money out of this account and into the destination account
        transferOut() {
            self.withdraw();
            DestinationAccount.deposit();
        }
   
        withdraw() {
            if (self.getBalance() < Amount) {
                throw new Error('Insufficient funds');
            }
            self.decreaseBalance(Amount);
        }
    }

    role DestinationAccount {
        deposit() {
            self.increaseBalance(Amount);
        }
    }

    role Amount {}
}


For more examples, see https://github.com/mbrowne/typescript-dci/tree/master/samples/dci/js.

Feel free to send me any questions or feedback.

James O Coplien

unread,
Mar 1, 2014, 5:06:26 AM3/1/14
to object-co...@googlegroups.com
On 28 Feb 2014, at 20:14, Harris Rodgers <harris....@gmail.com> wrote:

Maybe some background will help explain where I'm coming from on the user story/use case bdd test subject. I work as a developer on a team with a product owner, two user experience designer/developers and one other software developer. Every two weeks we set a goal, discuss and select some user stories from the backlog and plan a two week sprint. The backlog is primarily written by the product owner, but anyone on the team is empowered to contribute to it.

Much of your UX stuff (navigation trees, wireframe development) should be done by the PO. BDD tests should also be written by the PO — NOT the development team.

Interaction design and graphical design belong in the development team.


Our general process for working on a user story starts at the whiteboard. We spend a day or two (depending on scope) finding a solution and fitting it into the rest of the application. If we can, we go directly from whiteboard to code, but sometimes the UX designers will make higher fidelity mock-ups to help communicate our intended solutions to stakeholders off of the team.

Too late. Those should be enabling specifications developed by the PO, that the team reviews before going into the sprint. The PO is obliged to give the team an enabling specification of the feature, and prototypes are often part of that.

Prototypes are built to learn requirements. Why are the developers creating requirements? That's the PO's job.


Throughout or immediately after each conversation, I try to capture the primary business logic in the form of tests.

That's the PO's job (Source: Jeff Sutherland).


The tests normally describe a use of the application (not a traditional use case) and the data/roles/interactions that will be required.

This sounds a bit like a user narrative. Do you have one that you can show to us?


After reading about DCI it seemed like it would be an architectural pattern that would help us have better flexibility for this approach to problem solving.

In what regard is it more flexible than what you were doing before?


So far my very limited version of a dci framework which is a little more 'fully baked' than what I shared earlier has helped us move and refactor faster.

A user story is basically a scenario. DCI contexts encapsulate use cases. "Use case" is not Swedish for "scenario." A use case is a collection of related scenarios between an end user and a system, that work towards a common goal.

Example: Phone Call is a use case. One "user story" is that I dial a number, get audible ringback, after which the called party goes off-hook and we have a conversation, which ends when one of the parties goes on-hook. Another "user story" is that I can't remember the last half of the phone number halfway through dialing and I go onhook. Another is that the called party is already busy. Another is that in the middle of talking someone else tries to call one of us: I have my phone busy-don't-answer forwarded to the other party, and I also have call waiting. (Indeed: what happens?)

I am curious how you weave together multiple user stories that belong to one use case. This is a recurring blind spot in the user story world. Patton tried to bring people closer to the use case world with Story Maps but they fail to capture dependency information between the stories. That is an all-important consideration in DCI.

More important, user stories lack context. A use case provides context such as preconditions, postconditions, invariants, a system (!) goal (see Cockburn's "goal-driven use cases"). Even better than use cases, for setting context, are user narratives — usually done in conjunction with a persona. They're good conversation-starters. But a professional organisation will go the full mile and build user profiles based on market research. I know that not many organisations feel that they can afford to do this, so they end paying in complex designs (contexts become messy and complicated over time) and in maintenance.

Trygve Reenskaug

unread,
Mar 3, 2014, 4:10:50 AM3/3/14
to object-co...@googlegroups.com
On 2014.02.28 18:24, Andreas Söderlund wrote:
2014-02-28 17:36 GMT+01:00 Egon Elbre <egon...@gmail.com>:

+++

Role methods should be only resolved through direct invocation on Role, hence...

I haven't seen or heard that before, do you have a reference? Couldn't find it in the DCI execution model either. (http://fulloo.info/Documents/DCIExecutionModel-2.1.pdf)

Role method resolution is implementation dependent and therefore not mentioned in the paper on the execution model. There is no problem if there is role method injection as with Traits. The class has then been augmented and the runtime can always find the method. The situation is different with injectionless implementations. The role method is associated with the role and should only be triggered when the receiver object is playing that role. This is not as easy to determine as one might think, partly because an object doesn't know which roles it might be playing, if any. (An object might be playing several roles at the same time and doesn't play any role most of the time.)

 A solution is to look for the role method only if the receiver of message (invocation) is identified by its role. The choice between role method and instance method can then be done in the compiler.

Matthew Browne

unread,
Mar 3, 2014, 12:43:03 PM3/3/14
to object-co...@googlegroups.com
On Friday, February 28, 2014 2:14:33 PM UTC-5, Harris Rodgers wrote:
With regard to your concerns about callbacks and promises. I looked at your coffeescript project and I see what you mean. Since I'm working with a relatively small team I guess for the time being I will have to make it known that I don't currently support them. I think even without this style of thinking/coding has great advantages.

Just to be clear, callbacks and promises work correctly in any injectionless implementation, correct? As far as I can tell, they work fine in Egon's library and in my TypeScript implementation...

As to the other issues Andreas mentioned, I think that my implementation avoids those issues as well (with the exception of declaring a new local variable with the same name as a role), although it's certainly possible I might have missed something. The way it works is that for the ambiguous cases, it uses reflection at run-time since not all the information needed is available at compile-time. For example, in my library all of these are valid ways of calling the DestinationAccount.deposit() method:

DestinationAccount.deposit();
DestinationAccount['deposit']();
var methodName = '
deposit';
DestinationAccount[methodName]();
DestinationAccount.deposit.call(DestinationAccount);

I think at the very least, any Javascript DCI implementation should support DestinationAccount['deposit'](), since dot notation and bracket notation are always supposed to be equally valid ways of accessing functions (or any property) in Javascript.

In my implementation, since DestinationAccount.deposit() isn't ambiguous, it simply compiles to:
__context.__$DestinationAccount.deposit.call(__context.DestinationAccount);

(__context.__$DestinationAccount.deposit is the role method and __context.DestinationAccount is the data object currently playing the role.)

For DestinationAccount['deposit'](), it compiles to:
__dci_internal.getRoleMember(__context, __context.DestinationAccount, 'DestinationAccount', 'deposit')();

The getRoleMember function is called for all such ambiguous cases. The signature is:
getRoleMember(context, player, roleName, memberName)

If the memberName is a role method, then the role method is called, otherwise it's assumed to be a property of the data object. In cases like this, the role method call is a two-step process: (1) get a reference to the role method itself, i.e. a property lookup, and (2) call it. In order to make that work, I used Javascript's native Function.prototype.bind() method (introduced in ECMAScript 5, easily shimmable for earlier versions) to return a function bound to the current role player. This of course makes it possible for the programmer to do things they shouldn't, e.g. with the example Andreas and Egon were discussing, you could do this:

var d = Current.markVisited;
//suppose there are many lines of code here...

d();

That makes the code very hard to read and should be avoided, but I don't see any way of preventing the programmer from doing it without also preventing a legitimate call like this:
Current['mark' + 'Visited']();

Obviously that's a contrived example but I think there are a number of situations where calling role methods dynamically should be allowed.

I'm all for doing things at compile-time if the end result is the same without any loss in flexibility, but I think that in cases like these, calling the role method at run-time offers more flexibility and is more in line with DCI's focus on objects at run-time.

Egon Elbre

unread,
Mar 3, 2014, 1:08:13 PM3/3/14
to object-co...@googlegroups.com


On Monday, March 3, 2014 7:43:03 PM UTC+2, Matthew Browne wrote:
On Friday, February 28, 2014 2:14:33 PM UTC-5, Harris Rodgers wrote:
With regard to your concerns about callbacks and promises. I looked at your coffeescript project and I see what you mean. Since I'm working with a relatively small team I guess for the time being I will have to make it known that I don't currently support them. I think even without this style of thinking/coding has great advantages.

Just to be clear, callbacks and promises work correctly in any injectionless implementation, correct? As far as I can tell, they work fine in Egon's library and in my TypeScript implementation... 

As to the other issues Andreas mentioned, I think that my implementation avoids those issues as well (with the exception of declaring a new local variable with the same name as a role), although it's certainly possible I might have missed something. The way it works is that for the ambiguous cases, it uses reflection at run-time since not all the information needed is available at compile-time. For example, in my library all of these are valid ways of calling the DestinationAccount.deposit() method:

DestinationAccount.deposit();
DestinationAccount['deposit']();
var methodName = '
deposit';
DestinationAccount[methodName]();
DestinationAccount.deposit.call(DestinationAccount);

I think at the very least, any Javascript DCI implementation should support DestinationAccount['deposit'](), since dot notation and bracket notation are always supposed to be equally valid ways of accessing functions (or any property) in Javascript.


I disagree with that... roles should not have properties, hence accessing an element property should delegate to the object not the role. Also, I believe it would adversely affect readability of the code if that were to be allowed.

In my implementation, since DestinationAccount.deposit() isn't ambiguous, it simply compiles to:
__context.__$DestinationAccount.deposit.call(__context.DestinationAccount);

(__context.__$DestinationAccount.deposit is the role method and __context.DestinationAccount is the data object currently playing the role.)

For DestinationAccount['deposit'](), it compiles to:
__dci_internal.getRoleMember(__context, __context.DestinationAccount, 'DestinationAccount', 'deposit')();

The getRoleMember function is called for all such ambiguous cases. The signature is:
getRoleMember(context, player, roleName, memberName)

If the memberName is a role method, then the role method is called, otherwise it's assumed to be a property of the data object. In cases like this, the role method call is a two-step process: (1) get a reference to the role method itself, i.e. a property lookup, and (2) call it. In order to make that work, I used Javascript's native Function.prototype.bind() method (introduced in ECMAScript 5, easily shimmable for earlier versions) to return a function bound to the current role player. This of course makes it possible for the programmer to do things they shouldn't, e.g. with the example Andreas and Egon were discussing, you could do this:

var d = Current.markVisited;
//suppose there are many lines of code here...

d();


Also, what should happen if:

setTimeout(Current.markVisited, 100);
Current = somethingElse;

That makes the code very hard to read and should be avoided, but I don't see any way of preventing the programmer from doing it without also preventing a legitimate call like this:
Current['mark' + 'Visited']();

Why would you use that instead of Current.markVisited();


Obviously that's a contrived example but I think there are a number of situations where calling role methods dynamically should be allowed.

I doubt that... I believe there are cleaner alternatives in those situations.
 

I'm all for doing things at compile-time if the end result is the same without any loss in flexibility, but I think that in cases like these, calling the role method at run-time offers more flexibility and is more in line with DCI's focus on objects at run-time.


Loss of flexibility isn't always bad... thinking in simpler terms can make you write easier code.
 

rune funch

unread,
Mar 3, 2014, 1:25:41 PM3/3/14
to object-co...@googlegroups.com
> Den 03/03/2014 kl. 19.08 skrev Egon Elbre <egon...@gmail.com>:
>
>
> I disagree with that... roles should not have properties, hence accessing an element property should delegate to the object not the role. Also, I believe it would adversely affect readability of the code if that were to be allowed.

That would be a violation of the mental model of the language so I
strongly disagree. In JS any thing you access is a property and the
value returned might be a function. There're two equally valid ways of
accessing a property and they are semantically equal. Obj.propertyName
and obj['propertyName']. Since they are equal they should be treated
equal.

Egon Elbre

unread,
Mar 3, 2014, 1:59:45 PM3/3/14
to object-co...@googlegroups.com


On Monday, March 3, 2014 8:25:41 PM UTC+2, Rune wrote:
> Den 03/03/2014 kl. 19.08 skrev Egon Elbre <egon...@gmail.com>:
>
>
> I disagree with that... roles should not have properties, hence accessing an element property should delegate to the object not the role. Also, I believe it would adversely affect readability of the code if that were to be allowed.

That would be a violation of the mental model of the language so I
strongly disagree.

Roles do not exist in JS so those would be also in violation of the language, so that isn't a good argument.

In JS any thing you access is a property and the
value returned might be a function.

Also you can also access local/outer variables and arguments. And maybe something more that I forgot :).

There're two equally valid ways of
accessing a property and they are semantically equal. Obj.propertyName
and obj['propertyName']. Since they are equal they should be treated
equal. 

I can understand the inconsistency that it causes between X.method and X.method() and X['method']. I think it harms the readability of the code more than it benefits it. Also there is also an inconsistency that roles may not contain state, but objects can.

Maybe it's just that, currently, I do not see the role methods as being part of the role player.. I see role methods begin part of the role and hence the context. As an analogy, an actor is playing out a script that belongs to a larger play... but it isn't an usual behavior for the actor.

(PS. it isn't a rigid opinion, so it may change over time... or when I see that it is indeed useful instead of being harmful)

+ egon

Matthew Browne

unread,
Mar 3, 2014, 2:11:56 PM3/3/14
to object-co...@googlegroups.com
On 3/3/14 1:59 PM, Egon Elbre wrote:
I can understand the inconsistency that it causes between X.method and X.method() and X['method']. I think it harms the readability of the code more than it benefits it. Also there is also an inconsistency that roles may not contain state, but objects can.
Egon, you've made a number of good points, and some of my examples may go too far, but I still think that at least straightforward calls like DestinationAccount['deposit']() should be allowed and agree with what Rune said. If that were the only thing allowed, it could actually be done at compile time and my getRoleMember() function wouldn't be needed.

Maybe it's just that, currently, I do not see the role methods as being part of the role player.. I see role methods begin part of the role and hence the context. As an analogy, an actor is playing out a script that belongs to a larger play... but it isn't an usual behavior for the actor.

(PS. it isn't a rigid opinion, so it may change over time... or when I see that it is indeed useful instead of being harmful)
Maybe this is the crux of the issue...although technically, in an injectionless implementation, role methods belong to the context and not the object, to me the whole point of DCI is that the role methods do seem to belong to the object - temporarily of course. It's an illusion, but I think it's an illusion that's worth maintaining. I recall Cope posting something in a thread not to long ago to this effect, but I don't know if I necessarily understood him correctly and it may also just be his personal preference.

James Coplien

unread,
Mar 4, 2014, 3:40:49 AM3/4/14
to object-co...@googlegroups.com
I'm with Rune here. Violating the semantics of the base language in a way that's unreadable to outsiders is a no-no.

Good code readability is about not violating the Rule of Least Surprise.

That roles don't exist in JavaScript, and that we nonetheless simulate roles, is NOT an argument for syntactic inconsistency. That's silly.


Egon Elbre

unread,
Mar 4, 2014, 4:29:55 AM3/4/14
to object-co...@googlegroups.com
On Tuesday, March 4, 2014 10:40:49 AM UTC+2, Cope wrote:
I'm with Rune here. Violating the semantics of the base language in a way that's unreadable to outsiders is a no-no.

Hmm.. for me the only purpose of roles is to extend the behavior of object in some specific context.

But, maybe you are right... maybe I'm just not getting the intuitive explanation of role binding?

What should the intuitive analogy for "role bound to an object" be?

1. Actor playing a role, by reading, understanding a script and acting the behavior?
2. Actor embracing the role and memorizing the script and making it a part of him?
3. The role is just another perspective of an object that is always there, but only exposed in context?

From my perspective:
If using the (1) role playing analogy then the "Role['method']" should return the underlying objects property.
If using the (2) or (3) explanation then the "Role['method']" should return the role method.

Good code readability is about not violating the Rule of Least Surprise.

That roles don't exist in JavaScript, and that we nonetheless simulate roles, is NOT an argument for syntactic inconsistency. That's silly.

So the preferred approach would be to allow stateful roles by the "dci library" rather than break the language consistency? (And of course say in the documentation something like "you can do that, but don't do it".) Or alternatively throw some exception when trying to do stateful things with the role?

+ egon

rune funch

unread,
Mar 4, 2014, 5:10:41 AM3/4/14
to object-co...@googlegroups.com
In my view the following should hold:
if we have a role (ie an identifier) that's called foo and a role method for that role called bar then an invocation of bar on foo should invoke the role method on the role player at the time of invocation

In JS any object has a set of properties each of which can be accessed in two ways.
  1. by dot notation
  2. by and indexer
this is valid for any object including arrays and I see no reason why DCI should introduce "special objects" that violates this

I recently saw an example from a shop where they were only using indexers and never dot notation.

This is a problem for injectionless approaches since they usually do the magic at interpretation/compile time and in this case the identifier might not be know at that time since the indexer in JS can be and often is a variable.

I've had this problem in Marvin, interact and in maroon and haven't solved it in either this means that reflection on a role breaks the illusion of the methods belonging to the object. It can be resolved in all three cases but the amount of work that needs to go into it is substantial and I haven't had the energy to do it for any of them. It requires to hook into the type system and the type theory for DCI being underinvestigated ATM I've refrained from trying to do the work that is essentially grounds for a thesis while working on one of many spare time projects.

For JS I haven't found a way to do this in an injection less manner because in contrast to objects in .NET and Ruby the type of the object is external to the object and is changed through the type system but in prototype languages such as JS that's not true. The type it self is an object so hooking in to the "type system" requires injecting stuff into an object

-Rune




Egon Elbre

unread,
Mar 4, 2014, 5:28:04 AM3/4/14
to object-co...@googlegroups.com


On Tuesday, March 4, 2014 12:10:41 PM UTC+2, Rune wrote:
In my view the following should hold:
if we have a role (ie an identifier) that's called foo and a role method for that role called bar then an invocation of bar on foo should invoke the role method on the role player at the time of invocation

In JS any object has a set of properties each of which can be accessed in two ways.
  1. by dot notation
  2. by and indexer
this is valid for any object including arrays and I see no reason why DCI should introduce "special objects" that violates this

For me it's ok if Role identifiers are resolved/handled differently than simple variables... (in a similar way that you just can't do "a = {}; a();").


I recently saw an example from a shop where they were only using indexers and never dot notation.

This is a problem for injectionless approaches since they usually do the magic at interpretation/compile time and in this case the identifier might not be know at that time since the indexer in JS can be and often is a variable.

I've had this problem in Marvin, interact and in maroon and haven't solved it in either this means that reflection on a role breaks the illusion of the methods belonging to the object. It can be resolved in all three cases but the amount of work that needs to go into it is substantial and I haven't had the energy to do it for any of them. It requires to hook into the type system and the type theory for DCI being underinvestigated ATM I've refrained from trying to do the work that is essentially grounds for a thesis while working on one of many spare time projects.

For JS I haven't found a way to do this in an injection less manner because in contrast to objects in .NET and Ruby the type of the object is external to the object and is changed through the type system but in prototype languages such as JS that's not true. The type it self is an object so hooking in to the "type system" requires injecting stuff into an object

To me the implementation seems straightforward...

for some
// binding
Role = {};
// definition
Role: { foo: function(x,y,z){} , bar: function(){} };

e.g. rewrite to

Role$foo = function(x,y,z){};
Role$bar = function(){};
Role$$methodtable = {foo:Role$foo, bar:Role$bar};
Role$$get = function(v){
if(Role$$methodtable.hasOwnProperty(v)){
return Role$$methodtable[v];
}
return Role[v];
}

And in the code... all "Role.bar(x,y,z)" with "Role$bar.call(Role, x, y, z)", all "Role.bar" with "Role$bar" and all "Role[id]" with "Role$$get(id)". Of course this wouldn't allow assignment into a role method property.

+ egon

James Coplien

unread,
Mar 4, 2014, 5:37:03 AM3/4/14
to object-co...@googlegroups.com
On Tue, Mar 4, 2014 at 10:29 AM, Egon Elbre <egon...@gmail.com> wrote:
On Tuesday, March 4, 2014 10:40:49 AM UTC+2, Cope wrote:
I'm with Rune here. Violating the semantics of the base language in a way that's unreadable to outsiders is a no-no.

Hmm.. for me the only purpose of roles is to extend the behavior of object in some specific context.

That's an interesting paradigm and I wish you good fortune with it.

I'd also suggest checking out DCI. It's pretty cool. 

Egon Elbre

unread,
Mar 4, 2014, 6:36:52 AM3/4/14
to object-co...@googlegroups.com
On Tuesday, March 4, 2014 12:37:03 PM UTC+2, Cope wrote:

On Tue, Mar 4, 2014 at 10:29 AM, Egon Elbre <egon...@gmail.com> wrote:
On Tuesday, March 4, 2014 10:40:49 AM UTC+2, Cope wrote:
I'm with Rune here. Violating the semantics of the base language in a way that's unreadable to outsiders is a no-no.

Hmm.. for me the only purpose of roles is to extend the behavior of object in some specific context.

That's an interesting paradigm and I wish you good fortune with it.

That's more about the implementation rather than paradigm. I worded that badly...

The implementation corresponds to the interpretation of "1. Actor playing a role, by reading, understanding a script and acting out the behavior..."... i.e. actor is extending it's behavior in some specific context... (which is also suggested by "The role concept says nothing about the 
inner structure of an object but says everything about how the object is used together with 
other objects. The class concept says everything about the inner construction of an object but 
nothing about how it is used in its interaction with other objects."... but this is just cherry picking)
 
I'd also suggest checking out DCI. It's pretty cool. 

That's what I'm trying to clarify for myself. Which of the analogies (1), (2), (3) does correspond to DCI? Or is it something completely different? Or all of them?

I'm not able to understand from the DCI documents, nor the explanations I have yet seen.

I have this confusion, probably because all the [1]-[3] analogies are used. [1] "objects play a role in a context", [2] "the role methods are injected into the object" [3] "people have different roles that they play e.g. Father, Worker..."...

These, from my viewpoint, result in different implementations...

Maybe, I'm just wrong trying to draw an analogy between object and actors in theater?

+ egon

Matthew Browne

unread,
Mar 4, 2014, 7:23:05 AM3/4/14
to object-co...@googlegroups.com
On 3/4/14 5:28 AM, Egon Elbre wrote:
To me the implementation seems straightforward...

for some
// binding
Role = {};
// definition
Role: { foo: function(x,y,z){} , bar: function(){} };

e.g. rewrite to

Role$foo = function(x,y,z){};
Role$bar = function(){};
Role$$methodtable = {foo:Role$foo, bar:Role$bar};
Role$$get = function(v){
if(Role$$methodtable.hasOwnProperty(v)){
return Role$$methodtable[v];
}
return Role[v];
}

And in the code... all "Role.bar(x,y,z)" with "Role$bar.call(Role, x, y, z)", all "Role.bar" with "Role$bar" and all "Role[id]" with "Role$$get(id)". Of course this wouldn't allow assignment into a role method property.
That's essentially what my TypeScript implementation does. Also, within the role {} construct, data properties aren't allowed, so there's no chance of data properties inside the role. Of course setting properties on the data object should also be allowed, e.g.:

Customer['firstName'] = 'Fred';

I just realized that that breaks my implementation but I think the fix should be fairly simple: Have the compiler determine whether the bracket-notation property access is part of an assignment statement, and if so, rewrite it to __context.Foo[propertyName] = v;   (where __context.Foo is the role player). That way all assignments go directly to the data object and it's impossible to add them to the role itself, which should contain only methods.

Matthew Browne

unread,
Mar 4, 2014, 7:37:29 AM3/4/14
to object-co...@googlegroups.com
On 3/4/14 4:29 AM, Egon Elbre wrote:
So the preferred approach would be to allow stateful roles by the "dci library" rather than break the language consistency? (And of course say in the documentation something like "you can do that, but don't do it".) Or alternatively throw some exception when trying to do stateful things with the role?
My getRoleMember() function first searches for a role method, and if one is not found, then it assumes it's a data object property, and if not found throws an exception. Thinking about it now, I realized that it would break an if condition like:
foo['bar'] == undefined
so perhaps it shouldn't throw an error, but on the other hand, the error is descriptive and might be useful to the programmer more often than not.

The code is below...

On 3/4/14 5:10 AM, rune funch wrote:
For JS I haven't found a way to do this in an injection less manner because in contrast to objects in .NET and Ruby the type of the object is external to the object and is changed through the type system but in prototype languages such as JS that's not true. The type it self is an object so hooking in to the "type system" requires injecting stuff into an object
Why is that necessary? Where does my approach below fall short?


//Gets a member on a role player - can be either a role method or a method or property of the role player object
function getRoleMember(context: Object, player: Object, roleName: string, memberName: string) {
    if (player != context[roleName]) {
        //If we're here, it's because the programmer used `this` inside
        //a closure inside a role method.
        //(full explanation here: https://github.com/mbrowne/typescript-dci/blob/master/dci/dci.ts
        return player[memberName];
    }

    var roleMethod = context['__$' + roleName][memberName];
    if (roleMethod) {
        //bind the role player as `this` on the specified role method
        return roleMethod.bind(player);   
    }
    else {
        //check for property on role player
        if (!(
memberName in player)) {
            throw new Error('Method or property "' + memberName + '" not found on role "' + roleName + '" nor on its current role player.');
        }
        return player[memberName];
    }
}

Egon Elbre

unread,
Mar 4, 2014, 7:39:14 AM3/4/14
to object-co...@googlegroups.com


On Tuesday, March 4, 2014 2:23:05 PM UTC+2, Matthew Browne wrote:
On 3/4/14 5:28 AM, Egon Elbre wrote:
To me the implementation seems straightforward...

for some
// binding
Role = {};
// definition
Role: { foo: function(x,y,z){} , bar: function(){} };

e.g. rewrite to

Role$foo = function(x,y,z){};
Role$bar = function(){};
Role$$methodtable = {foo:Role$foo, bar:Role$bar};
Role$$get = function(v){
if(Role$$methodtable.hasOwnProperty(v)){
return Role$$methodtable[v];
}
return Role[v];
}

And in the code... all "Role.bar(x,y,z)" with "Role$bar.call(Role, x, y, z)", all "Role.bar" with "Role$bar" and all "Role[id]" with "Role$$get(id)". Of course this wouldn't allow assignment into a role method property.
That's essentially what my TypeScript implementation does. Also, within the role {} construct, data properties aren't allowed, so there's no chance of data properties inside the role. Of course setting properties on the data object should also be allowed, e.g.:

Customer['firstName'] = 'Fred';



Yes, the implementation that I showed works just like that, but it has an inconsistency of:

Customer.move;         // would return the role method
Customer.move = {};    // would assign the role method property [x]
Customer['move'];      // would return the role method
Customer['move'] = {}; // would assign the object data property [x]

Probably the [x] things should throw an exception instead.

James O. Coplien

unread,
Mar 4, 2014, 7:59:17 AM3/4/14
to object-co...@googlegroups.com, Thore Bjørnvig
This mail is long — even longer than it may seem at first reading.


On 04 Mar 2014, at 12:36, Egon Elbre <egon...@gmail.com> wrote:

That's what I'm trying to clarify for myself. Which of the analogies (1), (2), (3) does correspond to DCI? Or is it something completely different? Or all of them?

As the Buddhist monk said: Mu.

More below.


I'm not able to understand from the DCI documents, nor the explanations I have yet seen.

That's understandable.


I have this confusion, probably because all the [1]-[3] analogies are used. [1] "objects play a role in a context", [2] "the role methods are injected into the object" [3] "people have different roles that they play e.g. Father, Worker..."...

These, from my viewpoint, result in different implementations...

... all of which may be DCI ...


Maybe, I'm just wrong trying to draw an analogy between object and actors in theater?

... which is different than "metaphor [1]" to me, at least by default. Ooo. That got your attention. And this is a crucial distinction with crucial historical roots. Is the play scripted, or is it "play" in the sense of the overtly creative activities of a child? Is it both: in the Greek sense of "παίζειν"? (The Greeks used the same word for both theatre and for what children did and, unfortunately, seemed to ascribe a degree of scripting to both.) Or you can take the play metaphor literally, as some have (http://shakespearelang.sourceforge.net. Truly remarkable. Chihuahuas, indeed.)

Historically, we can argue that Kay's view is more that of children playing than of being scripted or subject to "programmed learning," which he hated. The PARC mantra of "if every object itself does the right thing then the system will work" was the popular Smalltalk community view of the 1980s. It was a great PR stunt, in spite of the fact that no one could ever quite figure out what "the right thing" was. Yet Kay's very model of object-orientation, based on the Simula object-as-process model, supports this view. (I do believe in this view and believe it may triumph some day in what Dick Gabriel calls "swarm computing," with hundreds of millions of objects interacting on short time scales. So far, in my life, I have not yet seen it for real.)

Our view in DCI is that use case design is human-centric and conscious rather than emergent. There are degrees of both self-organization and "do the right thing" in DCI, too, in its polymorphism model and its notion of classes.

A great play has elements of both. When Indiana Jones shoots the guy bearing two scimitars, it was a spontaneous reaction to the situation. It wasn't scripted. And it's one of the more memorable moments from the movie. Design is closely linked to creativity, creativity to divergent thinking, and divergent thinking to play. And if objects are supposed to have their roots in the kind of programming-for-children we find in LOGO, then we need to look more deeply into the human mind.

This is why I've been starting to delve into the psychology literature, and in particular the literature on children. I've also been re-visiting some of Alan Kay's very early stuff, and that's enlightening.

To Kay the Dynabook was a teaching tool. He contrasts the Dynabook with other teaching tools of the 1970s in that the Dynabook was about exploration and play, whereas most other tools were more Pavlovian in their approach ("programmed learning"). Kay at the time was very focused on Piaget's models of learning, especially from the 1950s, that suggested that children are, in essence, collection of what we call Contexts rather than collections of experiences or memories (more precisely, "declarative memories" of specific events). It turns out that more recent research has proven Piaget to be wrong about that (children form declarative memories very young).

He notes that children get bored not because they have short attention spans, but because they get stuck in playing one role for too long. The exploration paradigm requires role-switching. I think Kay sees this a bit differently than we do. He talks about sequencing as something that happens inside an object more so than he talks about what happens between objects. If you combine this with his insights into child role-switching, he must view an object as something that introspectively takes on different roles as processing proceeds from method to method.

So that's the original Kay model. You can (kind of) model Kay's model with a DCI meta-model (said more simply, you can implement Kay's model in DCI) but it's a much different view than we usually take here, and is more akin to each object having its own context.

Our model is much more like your "metaphor [1]" above, but, again, that's just a metaphor and an example. The example of the thing is not the whole thing.

What, then, is the "whole thing"?

Again, from a psychological perspective, this bears deeper exploration than I think we fully appreciate now. This is why I asked the question several weeks ago about whether there are any Smalltalk remnants (other than Object) that unify the notion of method and class. If so, that would be a perfect fit for Kay's 1970s-based, Piaget-rooted model that children beyond the age of two months are basically creatures of patterned time, or algorithm. If Kay had been right, then the notion that "the purpose of roles is to extend the behavior of objects" would be exactly backwards. It's the roles that are the meat of the matter and, to Kay, the exist *within* objects. It's curious that one can map this model onto "Rune's model" that is based on pure functional or applicative programming that unifies data and method.

But then several sticky problems arise. Child timelines are not observably discrete; methods are. (It's unfortunate but Kay very quickly made the leap to "algorithm": "What is an operational model if not an algorithm, a procedure for accomplishing a goal?", he says.) When one gets into this space we start to lack the vocabulary to describe any correspondences there might be between the children whom should be programmers, and anything we have in the computing world. So there is much work to be done.

The pressure points right now seem to be: 1. Mental models (a very well-defined concept in child psychology!) matter; 2. Playfulness in combining many things in different ways is important; 3. There is definitely something going on here with regard to the primacy of temporality (the "form of function"); 3. It is easy to get caught up in culture. My brother-in-law Thore Bjørnvig is in the process of publishing a piece on how Lego "filled the gap" in the 1970s when space exploration kind of came to a halt, and it's interesting to see how Lego's market blossomed during that period. Of course, it's easy to forget that it's the parents rather than the children who buy the Lego sets and it's possible that the children are living out their parents' fantasies of an ideal universe where nations expand into space hand-in-hand instead of fighting over Earthly realms. We may be snared by the same kinds of legacy knowledge. Until Rune came along no one questioned the assumption of a Von Neumann computational model. I found it was dangerous to challenge the assumption of a separation of function and data into two perspectives. Opening a psychological toolbox provides binoculars and telescopes that help cut through the fog of such perspectives to reason about computation in a much bigger universe.

In that universe, I think the phrase "objects play a role in a Context" is a good unfolding of DCI principles in the medium of contemporary programming languages and of broadly understood vocabulary. Every now and then I can see a glimmer of something more, or I find myself falling when I reach interesting cliffs. The most recurring of these cliffs relates to the relatively arbitrary separation of data and function, and that separation is in many ways at the roots of even being able to mutter: "objects play a role in a Context." I seek the deeper articulation.

Don't expect an answer any time soon; I suspect that I've embarked on what might be a ten-year effort before it bears fruit. On the other hand, it's often useful to ponder the big picture a bit when making decisions now (e.g., about how to do DCI in JavaScript) that are likely to stick around for ten years or so.

Why this long diatribe? I think it's crucial to understand that our metaphors are approximations and stakes in the ground. Each needs to be understood and evaluated in its historic context. You said you can't understand DCI from what you've read. Have you read the chapter the Trygve and I wrote in "Agile Software Architecture"? Have you read everything at http://fulloo.info/Documents/bibliography.html? Let me know what you *have* read and I can help you with an ordered list that might help you get to the deeper issues beneath your question (and I'm happy to send you the chapter from ASA). I honor the fact that your frustration is bumping up against these deeper issues in spite of the fact that you have been taught, maybe by this group, to seek the answers at the level of technique. I don't think you'll find them there.

Matthew Browne

unread,
Mar 4, 2014, 8:04:44 AM3/4/14
to object-co...@googlegroups.com
In my implementation I only rewrite Customer.move to __context.__$Customer.move if it's being called right there, otherwise it's rewritten to __context.Customer.move. That's not ideal however, because if you specify a name that's a role method, it should always return the role method if it exists, rather than assuming it's a property of the data object. Ideally I don't think the compiler should allow redefining a property with the same name as a role, i.e. Customer.move = {}; should throw a compiler error if Customer.move was declared as a role method. For getting a reference to a role method, e.g.:

var move = Customer.move;

...assuming that's allowed, I think it should be rewritten to:
var move = __dci_internal.getRoleMember(__context, __context.Customer, 'Customer', 'move');

That way it will return the role method if it exists, and otherwise look for a property 'move' on the data object.

Matthew Browne

unread,
Mar 4, 2014, 8:19:19 AM3/4/14
to object-co...@googlegroups.com
Still reading your post, but for now...


On 3/4/14 7:59 AM, James O. Coplien wrote:
Or you can take the play metaphor literally, as some have (http://shakespearelang.sourceforge.net. Truly remarkable. Chihuahuas, indeed.)
Haha, I love it! You might also enjoy:

http://byfat.xxx/if-hemingway-wrote-javascript

...the Shakespeare example there is great too.

Egon Elbre

unread,
Mar 4, 2014, 8:19:47 AM3/4/14
to object-co...@googlegroups.com
That's not necessary since you already know what role methods exist at preprocess time and you know the exact name of the method being asked. So you can either replace it as __context.Customer.move or __context.__$Customer.move.

James O. Coplien

unread,
Mar 4, 2014, 8:21:54 AM3/4/14
to object-co...@googlegroups.com

On 04 Mar 2014, at 14:04, Matthew Browne <mbro...@gmail.com> wrote:

Ideally I don't think the compiler should allow redefining a property with the same name as a role, i.e. Customer.move = {};


Maybe. I'd argue that it can be made to be perfectly well-defined behaviour based on scoping and extent rules, and that it's no more dangerous (from a readability perspective) than overriding a method in a derived class (and, in fact, it's safer, if the rules insist that the role method win over the instance method when called from another role method, and that the instance method win over the role method otherwise).

Matthew Browne

unread,
Mar 4, 2014, 9:17:38 AM3/4/14
to object-co...@googlegroups.com
Just so I understand...what if both methods belongs to the same role? Consider this example:

role Customer {
    move() {
        //Should these refer to the role method or the instance method?
        //If they refer to the role method, then how could the instance method be accessed?
        this.
foo();
        self.
foo();
       
        Customer.foo(); //role method
    }
   
   
foo() {
        console.log('
foo role method')
    }
}

Customer.foo = function() {
    console.log('foo instance method')
}


I'm not sure how to handle that situation; I thought that role methods always took precedence over instance methods with the same name...but I imagine that still being able to call the instance method could be useful in some situations, e.g. extending an existing instance method with a bit of additional functionality specific to the role.

James O. Coplien

unread,
Mar 4, 2014, 10:02:05 AM3/4/14
to object-co...@googlegroups.com

On 04 Mar 2014, at 15:17, Matthew Browne <mbro...@gmail.com> wrote:

I'm not sure how to handle that situation; I thought that role methods always took precedence over instance methods with the same name...but I imagine that still being able to call the instance method could be useful in some situations, e.g. extending an existing instance method with a bit of additional functionality specific to the role.

That means that roles would have to know about specifics of the object playing the role. That's O.K. to the extent that the object offers it's own API and if there's good distribution of responsibility between the role and the rest of the object. Overriding a role method in an instance method destroys the integrity of the context.

By the definition of DCI, there is no reasonable away to make this a safe situation. It would require that the class method obey the same contract (preconditions, postconditions, and class invariants) as the role method. If it did then one in theory could reason about Context execution in a way that overriding could not violate. But there is no way to guarantee this without putting a lot, lot, lot more limitations on the language.

I want the compiler to check (at compile time) if the possibility exists that a overridden method could violate this contract. If such a check is possible and if it passes, then you can get by with what you suggest. Where does that check take place? It can take place only at the point where a role and instance are associated. That implies that the compiler, at that point of association, must have enough *compile-time* knowledge of the type of the object (which won't exist until run time) to make such guarantees. That would either greatly restrict the statically declared types of objects that could be used as role-players: it's a much tighter restriction than just avoiding duck typing. The other alternative would be a complete contract system as in Eiffel, but even Eiffel's system would leave holes that could leave the Context assumptions to be unsafe.

I want to give the Context designer complete control over the corresponding system operation. Anything outside the Context is on the other side of an abstraction boundary. I am not only disallowed to look at it: it must be true that the stuff on the other side of the boundary can't upset my design assumptions. This is why we insist that the instance methods called from a role method be primitive: i.e., *extremely* high-context (in terms of what they are disallowed from doing).

rune funch

unread,
Mar 4, 2014, 10:19:51 AM3/4/14
to object-co...@googlegroups.com

2014-03-04 11:28 GMT+01:00 Egon Elbre <egon...@gmail.com>:
And in the code... all "Role.bar(x,y,z)" with "Role$bar.call(Role, x, y, z)", all "Role.bar" with "Role$bar" and all "Role[id]" with "Role$$get(id)". Of course this wouldn't allow assignment into a role method property.

In general yes that should work However Role.foo is not semantically equal to Role$foo. Role$foo is a free function where Role.foo semantically is an instance method or in functional lingo Role.foo is a partially applied function where the first parameter is bound to this/roleplayer. Role$foo has this bound to the global object and not the object the role is bound to. So Role.Foo should return the below function to keep the semantics

function(x,y,z) {
    return Role$foo.call(Role,x,y,z);
}

and similar goes for Role$$get  but I agree your general approach would work

Egon Elbre

unread,
Mar 4, 2014, 10:33:14 AM3/4/14
to object-co...@googlegroups.com


On Tuesday, March 4, 2014 5:19:51 PM UTC+2, Rune wrote:

2014-03-04 11:28 GMT+01:00 Egon Elbre <egon...@gmail.com>:
And in the code... all "Role.bar(x,y,z)" with "Role$bar.call(Role, x, y, z)", all "Role.bar" with "Role$bar" and all "Role[id]" with "Role$$get(id)". Of course this wouldn't allow assignment into a role method property.

In general yes that should work However Role.foo is not semantically equal to Role$foo. Role$foo is a free function where Role.foo semantically is an instance method or in functional lingo Role.foo is a partially applied function where the first parameter is bound to this/roleplayer.

In javascript "object.foo" returns the "non-partially-applied function", from consistency viewpoint it should do the same with "Role.foo". Although the "Role" inside the function is still bound to the role object regardless of the receiver of the eventual call of the function.

I.e. the receiver in a javascript method call is the thing before dot (or brackets), unless using call/apply... the method to be called default resolves is found from the prototype chain (unless the Role and method name match).

And, I do understand that returning a bound function is safer... and I've been thinking which approach would be better in practice.

rune funch

unread,
Mar 4, 2014, 10:36:37 AM3/4/14
to object-co...@googlegroups.com
That's one of the most valuable posts I've read for the last two years there's a few other gems fro you and Trygve but this one if different from those. The best metaphor I can find is that of "The unexpected journey" it's the begin of the hobit that has never ventured outside The shire and suddenly is forced to see the world in a new perspective. I do not think this journey has as many Ogres though but it sure feels like the start of a journey that will bring not just new insight but a completely new perspective

Thanks Cope.

--

rune funch

unread,
Mar 4, 2014, 10:54:48 AM3/4/14
to object-co...@googlegroups.com

2014-03-04 16:33 GMT+01:00 Egon Elbre <egon...@gmail.com>:
In javascript "object.foo" returns the "non-partially-applied function"

you are correct and my understanding was not

Egon Elbre

unread,
Mar 4, 2014, 11:35:25 AM3/4/14
to object-co...@googlegroups.com, Thore Bjørnvig
No, interestingly I haven't even noticed that book before.
No, only a few.
 
Let me know what you *have* read

The introductory/technical details/+"The Roots of DCI"  from http://fulloo.info/Documents/. Currently reading "Learn Architecture" (great book). Plus some from bibliography... but it's easier to assume I haven't read them properly.

Also, have watched most of the videos available online where you or Trygve talk.
 
and I can help you with an ordered list that might help you get to the deeper issues beneath your question (and I'm happy to send you the chapter from ASA).

This would be highly appreciated.

I honor the fact that your frustration is bumping up against these deeper issues in spite of the fact that you have been taught, maybe by this group, to seek the answers at the level of technique. I don't think you'll find them there.

I'm used to of learning in a way where I mix both... for me to properly learn DCI I would need something that empowers me to fully explore the ideas in it.. one part of that is an implementation where I can understand all the reasons why it's built that way. Of course this means that I will come against these issue.... and then I adjust my views to improve my own understanding.

Thank you for the reply.

+ egon

Matthew Browne

unread,
Mar 4, 2014, 12:01:03 PM3/4/14
to object-co...@googlegroups.com
On 3/4/14 10:33 AM, Egon Elbre wrote:
On Tuesday, March 4, 2014 5:19:51 PM UTC+2, Rune wrote:

2014-03-04 11:28 GMT+01:00 Egon Elbre <egon...@gmail.com>:
And in the code... all "Role.bar(x,y,z)" with "Role$bar.call(Role, x, y, z)", all "Role.bar" with "Role$bar" and all "Role[id]" with "Role$$get(id)". Of course this wouldn't allow assignment into a role method property.

In general yes that should work However Role.foo is not semantically equal to Role$foo. Role$foo is a free function where Role.foo semantically is an instance method or in functional lingo Role.foo is a partially applied function where the first parameter is bound to this/roleplayer.

In javascript "object.foo" returns the "non-partially-applied function", from consistency viewpoint it should do the same with "Role.foo". Although the "Role" inside the function is still bound to the role object regardless of the receiver of the eventual call of the function.

I.e. the receiver in a javascript method call is the thing before dot (or brackets), unless using call/apply... the method to be called default resolves is found from the prototype chain (unless the Role and method name match).

And, I do understand that returning a bound function is safer... and I've been thinking which approach would be better in practice.
You raise a good point that to be fully consistent with Javascript's normal behavior, the non-partially-applied function should be returned. One thing I think should definitely work is this:

Customer['move']()

Perhaps instead of rewriting that as:

__dci_internal.getRoleMember(__context, __context.Customer, 'Customer', 'move');

...and having getRoleMember return a function with this already bound to Customer, getRoleMember() should not do that and the call should be rewritten as:

__dci_internal.getRoleMember(__context, __context.Customer, 'Customer', 'move').call(Customer);

However if you did:
var move = Customer.move;
move();

It would be rewritten as:

var move = __dci_internal.getRoleMember(__context, __context.Customer, 'Customer', 'move');
move();

...and it would not work, just as that would not work in regular JS, because this wouldn't be bound to anything. Instead you'd need to do:
move.call(Customer);

call() and apply() should be avoided of course unless actually needed, e.g. if delegating arguments:
Customer.move.apply(Customer, arguments);

However, in the case of roles it would lead to unreadable code to allow the programmer to pass an alternate object as this via call or apply, i.e. this shouldn't be allowed:

var move = Customer.move;
move.call(someRandomObject);

If you want someRandomObject to play the Customer role, I think you should be required to actually bind it as the current role player...at least that's my current opinion.

Matthew Browne

unread,
Mar 4, 2014, 12:21:55 PM3/4/14
to object-co...@googlegroups.com
On 3/4/14 10:02 AM, James O. Coplien wrote:
I want the compiler to check (at compile time) if the possibility exists that a overridden method could violate this contract.
That statement is about an instance method overriding a role method, correct? In other words: If an instance method overrides a role method, the compiler would ensure that the instance method has the same signature (or possibly the same signature plus additional arguments) as the role method?

I still don't understand why you'd want to override a role method with an instance method in the first place...usually all the instance methods would exist on the object even before it is passed to the context. If you wanted a particular instance to behave differently you could just bind it to a different role. What I was thinking could be useful was the other way around -- overriding an instance method with a role method, but still having a way of calling the instance method from within that role. Does that make sense?

--
You received this message because you are subscribed to a topic in the Google Groups "object-composition" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/object-composition/QRgJp3N_VqY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to object-composit...@googlegroups.com.

Matthew Browne

unread,
Mar 4, 2014, 12:46:45 PM3/4/14
to object-co...@googlegroups.com
Just a quick note on what I said here:

One thing I think should definitely work is this:

Customer['move']()

Perhaps instead of rewriting that as:
__dci_internal.getRoleMember(__context, __context.Customer, 'Customer', 'move');

...and having getRoleMember return a function with this already bound to Customer, getRoleMember() should not do that and the call should be rewritten as:

__dci_internal.getRoleMember(__context, __context.Customer, 'Customer', 'move').call(Customer);
That may not have been the best example since a smart compiler could simply look up the 'move' method at compile time. But the getRoleMember() function would be needed for something like this:

var methodName = 'move';
Customer[methodName]()

James O. Coplien

unread,
Mar 4, 2014, 1:03:29 PM3/4/14
to object-co...@googlegroups.com
On 03 Mar 2014, at 19:59, Egon Elbre <egon...@gmail.com> wrote:

Maybe it's just that, currently, I do not see the role methods as being part of the role player.. I see role methods begin part of the role and hence the context. As an analogy, an actor is playing out a script that belongs to a larger play... but it isn't an usual behavior for the actor.

There is a recurring pattern on this group: We mix compile-and run time.

Roles do not exist at run time. I presume that "I do not see the role methods as being part of the role player" is meant to be a statement about run time. It's almost an oxymoron because roles don't exist at run time but role-players do. The reason I say "almost" is that you say role methods which, of course, are waiting in the wings at run time. But I think you hang yourself by asserting that role methods are "part of the role."

You do bring up an interesting perspective here. As a DCI programmer I situate myself inside of a Context looking around at the roles and looking outward at the paths through which role-players will come to me. There is a brief moment of magic at the beginning of the Context's life that sets things up and thereafter role methods become part of the objects. I think that's the core of the DCI computational model. That role methods live inside a Context is boilerplate — convenient boilerplate and packaging, to be sure, but at this level not part of my computational model (fish can't see water).

I think you're standing outside the Context looking at whether role methods live inside of it or outside of it. That's not an unreasonable compile-time view. But I should be thinking of the roles as instances when I read the code, just as it's impossible for me to read Shakespeare without substituting some kind of avatar for each of the roles. Role methods make no sense as dismebodied text — only when interpreted in the Context of a mental model that gives them reification through some kind of association.

In any case, this kind of reasoning seems to speak far too closely to implementation issues than I'm comfortable with. I don't yet have good alternative language. That's a challenge to everyone here.

James O. Coplien

unread,
Mar 4, 2014, 1:07:12 PM3/4/14
to object-co...@googlegroups.com
On 04 Mar 2014, at 18:21, Matthew Browne <mbro...@gmail.com> wrote:

On 3/4/14 10:02 AM, James O. Coplien wrote:
I want the compiler to check (at compile time) if the possibility exists that a overridden method could violate this contract.
That statement is about an instance method overriding a role method, correct?

Yes; I could have made that clearer.


In other words: If an instance method overrides a role method, the compiler would ensure that the instance method has the same signature (or possibly the same signature plus additional arguments) as the role method?

Er, not quite. The signatures have to match for it to be overriding at all; otherwise it's just "overloading across scopes" which is a ridiculous concept.

I want the compiler to detect this and to slam-dunk me if it finds any such instance. Better yet, I want the compiler not to implement this as overriding, but to keep role bindings in role space and class bindings in class space.


I still don't understand why you'd want to override a role method with an instance method in the first place...usually all the instance methods would exist on the object even before it is passed to the context.

I wasn't the one who asked, but if someone put a gun to my head I could probably concoct a reason.


If you wanted a particular instance to behave differently you could just bind it to a different role.

Yes, yes, yes!!! That's clear to me. It makes me wonder why this question of role method overriding persists — unless it's emblematic of a language bug.


What I was thinking could be useful was the other way around -- overriding an instance method with a role method, but still having a way of calling the instance method from within that role. Does that make sense?

Yuk. Example?

Matthew Browne

unread,
Mar 4, 2014, 2:05:28 PM3/4/14
to object-co...@googlegroups.com
On 3/4/14 1:07 PM, James O. Coplien wrote:
What I was thinking could be useful was the other way around -- overriding an instance method with a role method, but still having a way of calling the instance method from within that role. Does that make sense?

Yuk. Example?
Here's a simplified example from my class registration system (which is currently in PHP but I'm writing it how I would want it to look for my new TypeScript library). In the real implementation, since my PHP library doesn't allow role methods to override instance methods (due to limitations in PHP), I just named the role method Cart.addClassSecurely(). Perhaps that's a better approach anyway. But there have also been other times when I have wondered if it would be useful to use the same method name, as I have done below with addClass().


class Cart
{
    constructor() {
        this.classes = {};
    }
   
    addClass(cls: ScheduledClass) {
        this.classes[cls.id] = cls;
    }
   
    removeClass(classOrId) {
        var classId = (typeof classOrId == 'object' ? classOrId.id: classOrId);
        delete this.classes[classId];
    }
   
    ...
}

class SelectClasses extends DCI.Context
{
    /**
     * @param {Student} student
     * @param {Cart} shopping cart
     */
    bindRoles(student: Student, cart: Cart) {
        Student <- student;
        Cart <- cart;
    }
   
    //trigger method
    addClassToCart(cls: ScheduledClass) {
        Cart.addClass(cls);
    }
    ...
   
    role Cart {
        addClass(cls: ScheduledClass) {
            Registrar.holdSpotInClass(cls);
           
            //This is intended to be the call to the instance method,
            //but ordinarily I would think this.addClass() and self.addClass() would both
            //mean the role method, not the instance method...so I think maybe
            //an alternate syntax would be needed for this.
            this.addClass(cls);
        }
        ...
    }
   
    role Registrar {
        holdSpotInClass(cls: ScheduledClass) {
            ...
        }
        ...
    }
}

Matthew Browne

unread,
Mar 5, 2014, 9:35:15 AM3/5/14
to object-co...@googlegroups.com
I just realized that it may not be clear from that example why I chose to have a Cart role at all...the initial reason was because I have a role method Cart.getDisplayData() that loops through the items in the cart and prepares a simple data structure that gets passed to the view template to render the HTML for the cart. Since I needed that role anyway, it seemed to make sense to have methods on the Cart role for adding and removing classes too, even though it would have been possible to put:

Registrar.holdSpotInClass(cls);
Cart.addClass(cls);

...directly in the addClassToCart trigger method.

While a number of the descriptions for other use cases in the system are incomplete, I do have a description available for this one:
http://mbrowne.github.io/ClassRegistration-DCI/#/after-school-use-cases/select-classes
Reply all
Reply to author
Forward
0 new messages