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!
--
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.
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-dciBeing 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_workSo 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.
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...
The actual breaking behavior can be seen by adding line:var c = "Current.markVisited()";
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.
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:
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"?
Andreas, great response with lots of good information, thanks!
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.
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).
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).
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.
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.)
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.
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.
> 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.
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)
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.
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 invocationIn JS any object has a set of properties each of which can be accessed in two ways.
- by dot notation
- 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
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.
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.
To me the implementation seems straightforward...
for some// bindingRole = {};// 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.
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?
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
On 3/4/14 5:28 AM, Egon Elbre wrote: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.:To me the implementation seems straightforward...
for some// bindingRole = {};// 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.
Customer['firstName'] = 'Fred';
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?
Or you can take the play metaphor literally, as some have (http://shakespearelang.sourceforge.net. Truly remarkable. Chihuahuas, indeed.)
Ideally I don't think the compiler should allow redefining a property with the same name as a role, i.e. Customer.move = {};
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.
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.
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"
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.
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.
I want the compiler to check (at compile time) if the possibility exists that a overridden method could violate this contract.
--
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.
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);
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.
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?
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?