New version of native Javascript implementation

56 views
Skip to first unread message

Matthew Browne

unread,
Jul 3, 2015, 8:51:33 PM7/3/15
to object-co...@googlegroups.com
I have completed a new version of my native Javascript implementation:
http://jsfiddle.net/mbrowne/uvL1uk5a/2/

And for those who prefer to read the source for the library on its own:
https://gist.github.com/mbrowne/5fb7946fefd6b001ccbd

The implementation uses what I am calling "smart injection": role methods are injected into the object, but it still works if there are name conflicts between different roles/Contexts. (Note that role methods are not allowed to shadow/override instance methods; that would have been possible but I decided against it as per our previous discussions.)

This proves that it is possible to have an injectionful implementation that does not suffer from the usual "sticky injection" problem and which can handle role method name conflicts as well as injectionless implementations. There are still a couple "sticky injection" loopholes (described below), but by and large it works correctly, and those deficiencies are due to limitations in JS and not the technique itself (i.e. I could close those loopholes and still use the "smart injection" technique with the help of a little source transformation).

The syntax is much improved (thanks to Egon's suggestions):

var TransferMoney = dci.Context(function(source, destination, amount) {
    var transfer = dci.Roles({
        source: {
            $player: source,
            $contract: Account,
           
            withdraw: function() {
                transfer.source.decreaseBalance(transfer.amount);
            }
        },
       
        destination: {
            $player: destination,
            $contract: Account,
           
            deposit: function() {
                transfer.destination.increaseBalance(transfer.amount);
            }
        },
       
        amount: {
            $player: amount,
            $contract: Number
        }
    });
   
    //do the transfer
    transfer.source.withdraw();
    transfer.destination.deposit();
});


The implementation is nearly complete; I just need to do a bit more browser testing. (I am aware that it won't work in IE8 and below without syntax changes.)

There are two loopholes I'm aware of where it's possible to call role methods that should be out of scope:

#1:
externalFunction(roles.foo)

If you pass a role player to an external function in that way, then it would be possible to call foo() on the role player from the external function.

#2: Instance methods
It is currently possible to call role methods from instance methods (which of course it shouldn't be).

Note: I successfully implemented a version that prevents loophole #2, but it requires creating a proxy method for every instance method of every role player and I'm not sure if it's worth the performance impact. My current feeling is that it's not worth it given that it's not foolproof anyway (due to loophole #1, and of course the fact that a misguided programmer could pass the object containing the role getters outside the Context). (On the other hand, in many cases prototypal inheritance will have been used for data objects, so there would only be one copy of each instance method per data type...so at least memory-wise it might not be so bad.)

A note on garbage collection: I realized I didn't actually need to use WeakMap anywhere, so that's no longer a dependency. (I realized that the important thing is that the role players don't hold references to the Context; it's fine for the Context / reflection Context to hold references to the role players since the Context closure is holding references to them anyway, and in the case of stateful Contexts the role players are likely to be stored in properties anyway.)

As to reflection/debugging... I think I am storing about as much reflection info as can be safely stored without interfering with garbage collection -- which is still quite a lot of useful info (especially if you pass the optional 2nd and 3rd parameters to dci.Roles()). When inspecting an object, you can see all the roles to which it has been bound, and when inspecting a Context, you can see all its roles and current role bindings. The injected role information is organized by Context...to avoid interfering with garbage collection, a limited subset of Context reflection info is stored there (the names of its roles and role methods and optionally a reference to the Context function).

One other note: as per Rune's postulate, role re-binding is forbidden. To bind elements of a collection to a role within a loop, create a closure for each iteration of the loop and use that as an inline/nested Context. (I'm planning to create an example of that.)

I'll post the library on Github soon.

Please let me know if you have any feedback on this latest version!

Thanks,
Matt

Matthew Browne

unread,
Jul 3, 2015, 9:02:02 PM7/3/15
to object-co...@googlegroups.com
Also, I did a little experiment...the idea here is to highlight the interaction between 'source' and 'destination' without arbitrarily putting the transfer logic in the 'source' role:

    var transfer = dci.Roles({
        'source->destination': {
            $player: function doTransfer() {
                transfer.source.withdraw();
                transfer.destination.deposit();

            }
        }
   
        source: {
            $player: source,
            $contract: Account,
           
            withdraw: function() {
                transfer.source.decreaseBalance(transfer.amount);
            }
        },
       
        destination: {
            $player: destination,
            $contract: Account,
           
            deposit: function() {
                transfer.destination.increaseBalance(transfer.amount);
            }
        },
       
        amount: {
            $player: amount,
            $contract: Number
        }
    });
       
    //do the transfer
    transfer['source->destination']();


I suppose it's a little weird...and of course it could also be accomplished this way:

    transfer['source->destination'] = function() {
        transfer.source.withdraw();
        transfer.destination.deposit();
    }


Anyway, this probably isn't the best syntax, but perhaps there could be something analogous to the 'interaction' keyword in Marvin, which as Rune described it at one point ensures that all the roles for that interaction have been bound before the interaction can run. (Of course to actually ensure that would require a real parser, so this would be a better fit for the implementation I'm hoping to write for Babel).

I haven't formed a strong opinion on this yet (and maybe we don't even need something like this), but maybe this will provide food for thought...

Egon Elbre

unread,
Jul 4, 2015, 12:24:41 AM7/4/15
to object-co...@googlegroups.com


On Saturday, 4 July 2015 04:02:02 UTC+3, Matthew Browne wrote:
Also, I did a little experiment...the idea here is to highlight the interaction between 'source' and 'destination' without arbitrarily putting the transfer logic in the 'source' role:

    var transfer = dci.Roles({
        'source->destination': {
            $player: function doTransfer() {
                transfer.source.withdraw();
                transfer.destination.deposit();
            }
        }
   
        source: {
            $player: source,
            $contract: Account,
           
            withdraw: function() {
                transfer.source.decreaseBalance(transfer.amount);
            }
        },
       
        destination: {
            $player: destination,
            $contract: Account,
           
            deposit: function() {
                transfer.destination.increaseBalance(transfer.amount);
            }
        },
       
        amount: {
            $player: amount,
            $contract: Number
        }
    });

Add it as a regular function

transfer.interact = function() {
    transfer.source.withdraw();
    transfer.destination.withdraw();
};

transfer.interact();

Matthew Browne

unread,
Jul 4, 2015, 7:14:43 AM7/4/15
to object-co...@googlegroups.com
On 7/4/15 12:24 AM, Egon Elbre wrote:
Add it as a regular function

transfer.interact = function() {
    transfer.source.withdraw();
    transfer.destination.withdraw();
};

transfer.interact();
That works well in this particular case, but I was looking for a more generic solution. It works here because there's only one interaction, and the role methods don't contain their own interactions. But to continue along the lines of your suggestion, multiple interactions could be written like this:

transfer.interactions = {
    interaction1: function() {...},
    interaction2: function() {...}
};


But a big part of DCI is role methods interacting with other role methods - those are "interactions" too. I think the use of the word "interaction" to address this kind of scenario could be justified if it had some additional meaning like it does in Marvin. For example:

//"interaction" keyword ensures that both source and transfer are bound before it can be called
interaction transfer() {
    source.withdraw();
    destination.withdraw();
}


BTW, I made a small change to the dci.Context() function so that it creates a new object to use as this if this would have been undefined otherwise. So an alternative version of your suggestion could be:

this.interact = function() {
    transfer.source.withdraw();
    transfer.destination.deposit();
};
this.interact();


...just FYI, not that it's any better. (Actually it may be worse since it could give the impression that TransferMoney is a constructor function when it's not. Alternatively it could of course just be function interact() {...}; interact(); but I'll probably go with your suggestion).

Putting it all together, how about this:

function TransferMoney(source, destination, amount) {
    bank <- this;
    bank.transfer();

    role bank { 
        interaction transfer() {
            source.withdraw();
            destination.deposit();
        }
    }
       
    role source {
        withdraw() {
            self.decreaseBalance(amount);
        }
    }

    role destination {
        deposit() {
            self.increaseBalance(amount);
        }
    }

    role amount;
}


Or just:

function TransferMoney(source, destination, amount) {
    transfer();

    interaction transfer() {
        source.withdraw();
        destination.deposit();
    }
    ...
}


But I can't get very close to the above syntax using 'bank' in my native JS implementation, so I'll probably go with your interact() suggestion for the money transfer example.

Thinking more long-term, I still think some syntax to more explicitly express Rune's concept of multi-role methods could be useful.

Thanks,
Matt

James O Coplien

unread,
Jul 4, 2015, 7:20:06 AM7/4/15
to object-co...@googlegroups.com

On 04 Jul 2015, at 02:51, Matthew Browne <mbro...@gmail.com> wrote:

This proves that it is possible to have an injectionful implementation that does not suffer from the usual "sticky injection" problem and which can handle role method name conflicts as well as injectionless implementations.


Congratulations on a concrete demo of this. I hope that we can beat the hell out of it and in the end prove you right.

Can I suggest a free autographed copy of “Lean Architecture” to whomever finds the first significant bug (as judged by Matthew.) Matthew — is it ready for that?

Matthew excluded ;-)

Egon Elbre

unread,
Jul 4, 2015, 7:49:33 AM7/4/15
to object-co...@googlegroups.com
Note that some of the problems have already been discussed...


i.e. undefined, null, 0.0, 10, "xyz" cannot be a role player with methods.

Those are known issues - it's tied to the fact that those types cannot have own methods. Matthew, add explicit checks for those types to make behavior less surprising.

+ Egon

Rune Funch Søltoft

unread,
Jul 4, 2015, 1:42:17 PM7/4/15
to object-co...@googlegroups.com


> Den 04/07/2015 kl. 13.49 skrev Egon Elbre <egon...@gmail.com>:
>
> i.e. undefined, null, 0.0, 10, "xyz" cannot be a role player with methods.
>
> Those are known issues - it's tied to the fact that those types cannot have own methods. Matthew, add explicit checks for those types to make behavior less surprising

Might have missed it but Why not coerce them into real objects? That's part of the spec already in several cases. Ie (function(){return this;}).call(1); will do just that. Yes that will result in a new identity since there's only one 1 literal. However that identity issue is native to JS so it's not introduced by this

Matthew Browne

unread,
Jul 4, 2015, 1:58:13 PM7/4/15
to object-co...@googlegroups.com
I'll send a fuller reply when I'm back at my computer, but yes, that's how the implementation is supposed to work and it was working in an earlier version. Thanks for pointing out the issue Egon; it was a minor scoping bug. I'll post the fix later today.

To clarify, numbers are converted to Number objects, strings to String objects, and booleans to Boolean objects. Yes that means the identity is no longer the same but == comparisons should still work and primitive types are immutable in JS anyway, so it's not like you could modify a primitive within a Context anyway.
--
Sent from my Android device with K-9 Mail.

Egon Elbre

unread,
Jul 4, 2015, 2:13:30 PM7/4/15
to object-co...@googlegroups.com
Gist of the problem is that:

var x = 1;
(function(){return this;}).call(x) === (function(){return this;}).call(x); 
// return false

is surprising

It basically has all the problems of a wrapper e.g...

var Equals = dci.Context(function(A, B) {
var equals = dci.Roles({
alpha: {
$player: A,
compare: function(){
return equals.alpha === equals.beta
}
},
beta: {
$player: B,
}
});
return equals.alpha.compare()
});

Equals(x,x)
// returns false

It's better not to automatically wrap things, as it probably creates more problems than it solves.

+ Egon

Rune Funch Søltoft

unread,
Jul 4, 2015, 5:37:35 PM7/4/15
to object-co...@googlegroups.com


Den 04/07/2015 kl. 20.13 skrev Egon Elbre <egon...@gmail.com>:


var x = 1;
(function(){return this;}).call(x) === (function(){return this;}).call(x); 
That shouldn't be to surprising to someone that has actually familiarised himself with the specs (which I until recently hadn't as one might see on SO) and it's also only partly true that the expression returns false. If it's called with in at strict environment it returns true. My point is that it doesn't add confusion to coerce the liberals into objects and there's plenty confusion to be found in JS already which we can't remove anyhow

Matthew Browne

unread,
Jul 4, 2015, 5:42:59 PM7/4/15
to object-co...@googlegroups.com
On 7/4/15 7:20 AM, James O Coplien wrote:
Can I suggest a free autographed copy of “Lean Architecture” to whomever finds the first significant bug (as judged by Matthew.)
Nice :)

Matthew — is it ready for that?
Yes, I think it's ready for stress testing now.

For the most part it works correctly according to my own testing...I think the main issue with it is the verbosity, especially with role / instance members that would ideally be accessible with 'self' or 'this'. 'this' does point to the current object, but it can only be used to access instance members instead of both instance members and role methods. So in the documentation I will recommend avoiding the use of this, for the sake of consistency, except when passing this as an argument to another method or function to avoid the loophole #1 I described in my original post in this thread. Unfortunately there is no way to provide a self or this variable that works correctly and reliably without source transformation.

Matthew Browne

unread,
Jul 4, 2015, 5:48:37 PM7/4/15
to object-co...@googlegroups.com
One other note with regard to the verbose syntax...it would be possible to support this syntax:

source().withdraw();

instead of:

roles.source.withdraw();
or
transfer.source.withdraw();
etc...

(That would also make it possible to provide compatibility with IE8 and below.)

But I think the function call (i.e. the parentheses) is misleading, so I'm going to stick with the current syntax for all the examples and only mention this as a side-note.

Matthew Browne

unread,
Jul 4, 2015, 6:14:07 PM7/4/15
to object-co...@googlegroups.com
Here is a corrected version that works as I intended:
http://jsfiddle.net/mbrowne/uvL1uk5a/4/
That shouldn't be to surprising to someone that has actually familiarised himself with the specs (which I until recently hadn't as one might see on SO) and it's also only partly true that the expression returns false. If it's called with in at strict environment it returns true. My point is that it doesn't add confusion to coerce the liberals into objects and there's plenty confusion to be found in JS already which we can't remove anyhow --
I'm leaning toward agreeing with Rune on this. It certainly is a lot more convenient to set a number or string as a role player and have it just work. I can see Egon's point that the programmer isn't expecting their number or string to be converted to a new Number() or String() object since it happens behind the scenes. Also, the following scenario might be confusing: 'transfer.amount' is assigned a new value, but 'amount' still holds the original value. But now that I think about it, I have a setter method for the roles object already, so I could just add a condition in the setter method to update the original number whenever the Number role-player changes. And role players are supposed to be objects, so I think it's reasonable for the environment to coerce primitives to objects automatically.

But if I were to agree with Egon, then I could have it throw an error instructing the programmer to create their own Number(), String() or Boolean() object.

Now, as to null and undefined...

I have handled both in a deliberate way. Null is not really an object, so methods can't be added to it, and I throw an error if the programmer tries to bind null to a role with methods (binding it to a role with no methods is fine); I think this is reasonable behavior. I could have treated undefined in the same way, but I can't see why you would ever want to bind undefined to a role with methods in the first place, and I also am using undefined to indicate a role that has not yet been bound; this allows a role to be bound later rather than requiring that you specify a $player in the role definition. An alternative would be to check ('$player' in roleDef) instead of (roleDef.$player === undefined), but if the programmer's app is trying to bind undefined to a role with methods, isn't that an error anyhow? I suppose it might be worth changing it just to give a better error message though...

P.S. I realized another change that would be useful...the __dciReflectionData and __roles properties used to store reflection info should probably be non-enumerable properties. I'll leave it as is on jsfiddle since having them enumerable probably makes it easier to play around with and see what's going on. But in practice I wouldn't want to interfere with iterating over object properties, etc.

Egon Elbre

unread,
Jul 5, 2015, 6:53:00 AM7/5/15
to object-co...@googlegroups.com


On Sunday, 5 July 2015 01:14:07 UTC+3, Matthew Browne wrote:
Here is a corrected version that works as I intended:
http://jsfiddle.net/mbrowne/uvL1uk5a/4/

On 7/4/15 5:37 PM, Rune Funch Søltoft wrote:
Den 04/07/2015 kl. 20.13 skrev Egon Elbre <egon...@gmail.com>:


var x = 1;
(function(){return this;}).call(x) === (function(){return this;}).call(x); 
That shouldn't be to surprising to someone that has actually familiarised himself with the specs (which I until recently hadn't as one might see on SO) and it's also only partly true that the expression returns false. If it's called with in at strict environment it returns true. My point is that it doesn't add confusion to coerce the liberals into objects and there's plenty confusion to be found in JS already which we can't remove anyhow --
I'm leaning toward agreeing with Rune on this. It certainly is a lot more convenient to set a number or string as a role player and have it just work. I can see Egon's point that the programmer isn't expecting their number or string to be converted to a new Number() or String() object since it happens behind the scenes. Also, the following scenario might be confusing: 'transfer.amount' is assigned a new value, but 'amount' still holds the original value. But now that I think about it, I have a setter method for the roles object already, so I could just add a condition in the setter method to update the original number whenever the Number role-player changes. And role players are supposed to be objects, so I think it's reasonable for the environment to coerce primitives to objects automatically.

But if I were to agree with Egon, then I could have it throw an error instructing the programmer to create their own Number(), String() or Boolean() object. 

I would throw an error only if that role definition has methods - i.e. make as much work as possible without implicit wrapping.

The implicit wrapping approach changes the result of "typeof" operator, a lot of code has assertions such as assert(typeof x === "number"), this means that the value playing the role and the actual input value will behave differently in there. It's an easy way to get small bugs.

Inside the context, I don't think the wrapping will be a problem, but I'm seriously concerned when passing those values forward into some library X.


Now, as to null and undefined...

null and undefined are values as well, so making them "special" makes things less orthogonal. But I agree that it is a problem in JS itself.

A rule "You cannot use role methods with a value type (string, number, null, boolean, undefined)", would be much clearer and makes less likely to hit a subtle bug.

Rune Funch Søltoft

unread,
Jul 5, 2015, 9:13:47 AM7/5/15
to object-co...@googlegroups.com


> Den 05/07/2015 kl. 12.53 skrev Egon Elbre <egon...@gmail.com>:
>
> null and undefined are values as well
Undefined is an identifier that points to something. What it points to can be changed depending on scope because undefined is not a keyword. So the below function is valid
function(undefined){ ... } and in that scope undefined can be anything. Including an object with methods.

Rune Funch Søltoft

unread,
Jul 5, 2015, 9:18:12 AM7/5/15
to object-co...@googlegroups.com


> Den 05/07/2015 kl. 12.53 skrev Egon Elbre <egon...@gmail.com>:
>
> I would throw an error only if that role definition has methods - i.e. make as much work as possible without implicit wrapping.
>
> The implicit wrapping approach changes the result of "typeof" operator, a lot of code has assertions such as assert(typeof x === "number"), this means that the value playing the role and the actual input value will behave differently in there. It's an easy way to get small bugs.
>
> Inside the context, I don't think the wrapping will be a problem, but I'm seriously concerned when passing those values forward into some library X.
The specifications are pretty clear on how to treat a literal/value when used as an object. Your suggestion is to not follow those rules. To me that's violating the rule of least surprise after all what we are talking about is simply another case of using a value as an object. I would be surprised if this particular case was inconsistent with all the other cases

Matthew Browne

unread,
Jul 5, 2015, 10:27:49 AM7/5/15
to object-co...@googlegroups.com
Yes, Javascript lets you do that, but it's a really bad idea...proper JS
usage is to not create any variables called 'undefined', especially not
a global one. In fact in my library I assumed that checking someVal ===
undefined was a valid way of checking if a value is undefined. I think
most browsers now prevent you from creating a global variable called
'undefined', but if I wanted to be paranoid about browser compatibility
and incorrect usage I could have checked for (typeof someVal ===
'undefined') instead.

Matthew Browne

unread,
Jul 5, 2015, 10:36:01 AM7/5/15
to object-co...@googlegroups.com
I think Egon meant something like this:

function TransferMoney(sourceAccount, destinationAccount, amount) {
    ...
    test(transfer.amount);
}

function test(amount) {
    typeof amount == 'number' //false
}

var amt = 10;
(typeof amt == 'number') //true
TransferMoney(src, dst, amt);



I can of course mention in the docs that all primitives will be coerced into objects, but I do agree with Egon that this could be surprising and introduce subtle bugs in some cases.

A compromise would be to coerce primitives into objects only if the programmer bound them to a role containing methods - and otherwise (i.e. for a role with no methods) leave them alone. But rather than making the behavior inconsistent, then maybe it would be better to throw an error when attempting to bind a primitive to a role with methods, suggesting that they convert it to an object first.

Egon Elbre

unread,
Jul 5, 2015, 10:42:38 AM7/5/15
to object-co...@googlegroups.com


On Sunday, 5 July 2015 16:18:12 UTC+3, Rune wrote:


> Den 05/07/2015 kl. 12.53 skrev Egon Elbre <egon...@gmail.com>:
>
> I would throw an error only if that role definition has methods - i.e. make as much work as possible without implicit wrapping.
>
> The implicit wrapping approach changes the result of "typeof" operator, a lot of code has assertions such as assert(typeof x === "number"), this means that the value playing the role and the actual input value will behave differently in there. It's an easy way to get small bugs.
>
> Inside the context, I don't think the wrapping will be a problem, but I'm seriously concerned when passing those values forward into some library X.
The specifications are pretty clear on how to treat a literal/value when used as an object.

No one reads the specifications :)
 
Your suggestion is to not follow those rules. To me that's violating the rule of least surprise after all what we are talking about is simply another case of using a value as an object. I would be surprised if this particular case was inconsistent with all the other cases

I agree, it's inconsistent with the spec and chooses a stricter approach that is less likely to create subtle bugs.

+ Egon

Egon Elbre

unread,
Jul 5, 2015, 10:59:23 AM7/5/15
to object-co...@googlegroups.com


On Sunday, 5 July 2015 17:36:01 UTC+3, Matthew Browne wrote:
On 7/5/15 9:18 AM, Rune Funch Søltoft wrote:

      
Den 05/07/2015 kl. 12.53 skrev Egon Elbre <egon...@gmail.com>:

I would throw an error only if that role definition has methods - i.e. make as much work as possible without implicit wrapping.

The implicit wrapping approach changes the result of "typeof" operator, a lot of code has assertions such as assert(typeof x === "number"), this means that the value playing the role and the actual input value will behave differently in there. It's an easy way to get small bugs.

Inside the context, I don't think the wrapping will be a problem, but I'm seriously concerned when passing those values forward into some library X.
The specifications are pretty clear on how to treat a literal/value when used as an object. Your suggestion is to not follow those rules. To me that's violating the rule of least surprise after all what we are talking about is simply another case of using a value as an object. I would be surprised if this particular case was inconsistent with all the other cases

I think Egon meant something like this:

function TransferMoney(sourceAccount, destinationAccount, amount) {
    ...
    test(transfer.amount);
}

function test(amount) {
    typeof amount == 'number' //false
}

var amt = 10;
(typeof amt == 'number') //true
TransferMoney(src, dst, amt);



I can of course mention in the docs that all primitives will be coerced into objects, but I do agree with Egon that this could be surprising and introduce subtle bugs in some cases.

Yes, code such as

function gcd(x, y){
    assert.int(x);
    assert.int(y);
    x = x|0; y = y|0;
    // ...
}

is not uncommon. I can vaguely remember Dart generating such statements in some mode. It might have been TypeScript as well, but not sure.

Rune Funch Søltoft

unread,
Jul 5, 2015, 12:38:51 PM7/5/15
to object-co...@googlegroups.com
Or as I stated previously. Follow the ECMAScript specification that says that when a value is used as an object the value is coerced. In also states that in some cases the programmer gets to choose. Eg the example I gave can have both behaviours depending on whether its a strict context or not but again my point is that I think we should do as the specifications whether we agree with them or not. If we can't honour the specs and the DCI semantics then the platform doesn't support DCI and we can't change that fact by not honouring the specs

Mvh
Rune
--
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/d/optout.

Rune Funch Søltoft

unread,
Jul 5, 2015, 12:43:29 PM7/5/15
to object-co...@googlegroups.com


> Den 05/07/2015 kl. 16.42 skrev Egon Elbre <egon...@gmail.com>:
>
>
> I agree, it's inconsistent with the spec and chooses a stricter approach that is less likely to create subtle bugs
Any proof that violating the specs does not produce bugs and that those that created the specs were ignorant to the fact that the specs are the likely source of subtle bugs. It is even a stricter one it's simply a different approach.

James O Coplien

unread,
Jul 5, 2015, 2:38:38 PM7/5/15
to object-co...@googlegroups.com

On 05 Jul 2015, at 00:14, Matthew Browne <mbro...@gmail.com> wrote:

I'm leaning toward agreeing with Rune on this. It certainly is a lot more convenient to set a number or string as a role player and have it just work.

It depends whether the purpose is research or production.

For research, I agree,

For production, the expense of treating values as objects, and of cloning each one, will provide a very unpleasant surprise for anyone dealing with big data.

James O Coplien

unread,
Jul 5, 2015, 2:43:33 PM7/5/15
to object-co...@googlegroups.com

On 05 Jul 2015, at 18:38, Rune Funch Søltoft <funchs...@gmail.com> wrote:

Eg the example I gave can have both behaviours depending on whether its a strict context or not but again my point is that I think we should do as the specifications whether we agree with them or not. If we can't honour the specs and the DCI semantics then the platform doesn't support DCI and we can't change that fact by not honouring the specs

Isn’t it the case that we can find a disagreement between DCI and the spec of any given base language, given any base language?

This, of course, is why I’ve chosen the path on which I currently find myself. I won’t disparage those pursuing other paths, but I would point out that this is an extremely important consideration where we have long been lacking in pointed advice. Short of there being pointed decisions on a per-platform basis  that make the compromises explicit, the short-term approximations to DCI will fail. I have a fairly high degree of confidence that any attempt at cross-fertizilization into an existing platform cannot avoid these compromises.

Matthew Browne

unread,
Jul 5, 2015, 8:20:38 PM7/5/15
to object-co...@googlegroups.com
On 7/5/15 2:38 PM, James O Coplien wrote:
It depends whether the purpose is research or production.

For research, I agree,

For production, the expense of treating values as objects, and of cloning each one, will provide a very unpleasant surprise for anyone dealing with big data.
I hope for it to be used for production.

And in response to Rune:
If we can't honour the specs and the DCI semantics then the platform doesn't support DCI and we can't change that fact by not honouring the specs
Well first of all, we're still formalizing the DCI semantics...but to the extent that we have established them, why is it a violation to say that primitive types must be manually converted into objects in order to play roles? JS includes object versions of every primitive, so it's not like it's that burdensome for the programmer to coerce their own objects.

And what exactly did you mean by "treat a literal/value when used as an object"? You can do things like "xyz".substr(1) and I would say that's treating it as an object, or you can do new String("xyz") and it actually becomes a real object. So I think there's some gray area as to what binding a primitive to a role should do.

I'm currently thinking that what I said in my previous post may be the best option:

maybe it would be better to throw an error when attempting to bind a primitive to a role with methods, suggesting that they convert it to an object first.
...I guess it would still be a little inconsistent, but if I go with that option then I could also allow primitives to play roles with no methods, without needing to coerce them to objects. That's probably how they'll be used most of the time (as roles without methods) so I think it makes sense to go with what works most naturally for that, and it avoids nasty surprises like what Egon mentioned.

Matthew Browne

unread,
Jul 5, 2015, 8:38:03 PM7/5/15
to object-co...@googlegroups.com
Yes, there will always be compromises with existing languages. With source transformation we can get pretty close (not all the way there when considering reflection/debugging), but source transformation itself is a compromise.

For those of us doing web development, we have to deal with Javascript in one way or another, so we have to live with at least some compromises. After we have a real DCI language like Trygve, someone could write a library to compile it to JS, and it would be possible to use one language for both server-side and client-side, with the server-side having all the DCI goodies that we want. But the generated JS would probably be quite far from regular JS, making it harder to debug. So I think it's important to continue working on DCI in JS itself. Adding some simple extensions to the language using source transformation is a pretty good option, and down the road we could even fork Google's JS engine, V8, which would make it possible to truly support DCI in both node.js/ io.js and in Chrome. In the case of Chrome it would only be on the developer's machine, but source transformation could make it work everywhere else. And the main shortcoming of source transformation is in debugging, so being able to debug in Chrome on one's own machine would fill that gap fairly well.

But outside the realm of web development involving the web browser, we have a lot more options, and I look forward to seeing Trygve and where it leads us.

Rune Funch Søltoft

unread,
Jul 6, 2015, 2:32:12 AM7/6/15
to object-co...@googlegroups.com


> Den 06/07/2015 kl. 02.20 skrev Matthew Browne <mbro...@gmail.com>:
>
> Well first of all, we're still formalizing the DCI semantics...but to the extent that we have established them, why is it a violation to say that primitive types must be manually converted into objects in order to play roles? JS includes object versions of every primitive, so it's not like it's that burdensome for the programmer to coerce their own objects
First of all I was referring to the semantics you are trying to implement :). I simply disagree with the notion that coercing the values into objects should be the source of a new class of bugs. It's done a lot by the environment already and thus the potential for those bugs are already present regardless of how you implement roles for native types. In JS if you bind a value to the this "role" or the object "role" of an object expression JS will coerce. Why would other bindings have to perform differently? If an value is bound to a role I would say that the implementation should honour that JS uses values as values of the programmer use them as values and objects if they are used as objects. That's how JS works and doing things different will indeed lead to a new class of subtle bugs

Matthew Browne

unread,
Jul 6, 2015, 7:14:43 AM7/6/15
to object-co...@googlegroups.com
I think there's a difference between what happens normally in JS and
what we're considering here...

Normal JS:
var str = "xyz"
str.substr(1) //treat it as an object
typeof str == 'string' // true

DCI (with automatic coercion):
roles.str = "xyz";
typeof roles.str == 'string' // false

rune funch

unread,
Jul 6, 2015, 8:30:38 AM7/6/15
to object-co...@googlegroups.com


2015-07-06 13:14 GMT+02:00 Matthew <mbro...@gmail.comBrowne> :
I think there's a difference between what happens normally in JS and what we're considering here...

Normal JS:
var str = "xyz"
str.substr(1) //treat it as an object
typeof str == 'string'   // true

DCI (with automatic coercion):
roles.str = "xyz";
typeof roles.str == 'string'  // false


I agree there's a difference and I don't think there should be. The first is semantically the same as

var str = "xyz";
new String(str).substring(1);
typeof str === 'string'

and the second is

roles.str = new String("xyz")
typeof roles.str === 'string'

what I'm saying is that in the first case there is a transcient context. we could rewrite it to


var str = "xyz"
(function(){
    this.substr(1);
    //typeof this === 'object' is true
}).call(str);
where str is bound to the single role called this.
and if you within that context tests the typen of 'this' you will indeed find that it is an object

JS let's the programmer decide whether this is to be so. In the below case 'this' is of type string

var str = "xyz"
(function(){
    "use strict";
    this.substr(1);
    //typeof this === 'string' is true
}).call(str);

Matthew Browne

unread,
Jul 6, 2015, 8:39:34 AM7/6/15
to object-co...@googlegroups.com
As far as I can tell, the behavior of strict mode is how the ECMAScript committee thinks Javascript (ECMAScript) thinks the language probably should have behaved in the first place. It's a way to correct earlier mistakes without breaking backward compatibility.

So if we're going to use the spec as our guide, I think we should base it on how it works in strict mode. But as Egon mentioned, almost no one reads the spec (I haven't read it myself and I seem to be getting by fine doing JS development, thanks to a lot of tutorials and other online resources over the years).

Rune Funch Søltoft

unread,
Jul 6, 2015, 9:02:25 AM7/6/15
to object-co...@googlegroups.com


> Den 06/07/2015 kl. 14.39 skrev Matthew Browne <mbro...@gmail.com>:
>
> So if we're going to use the spec as our guide, I think we should base it on how it works in strict mode.
I agree that if you have to chose one or the other you should chose strict mode. However that does not affect the point I'm trying to make. When a literal is used as an object it's coerced to become an object. It's not the literal. It's mostly transient (aside from .call) and that's how I think it should be with roles. If the value is used it's a value of its however used as an object it's an object. Than means i believe it should be possible to bind a value and be able to treat it as an object when required

Matthew Browne

unread,
Jul 6, 2015, 9:11:28 AM7/6/15
to object-co...@googlegroups.com
Unfortunately I have to pick one or the other in this implementation.
When the programmer requests a role, e.g. roles.str, I don't know in
advance whether they're going to try to call a method on it or not. I
either have to return a primitive or an object, before knowing what
they're going to do with it.
Reply all
Reply to author
Forward
0 new messages