Re: Method overrides

194 views
Skip to first unread message

James O. Coplien

unread,
May 13, 2016, 2:43:45 PM5/13/16
to object-co...@googlegroups.com
Matthew raises the following issue:


On 13 May 2016, at 17:42, Matthew Browne <mbro...@gmail.com> wrote:

class Animal {}
class Cat extends Animal {}
class Dog extends Animal {}

class AnimalShelter {
    public void putAnimal(Animal a) {
        System.out.println("AnimalShelter.putAnimal");
    }
}

class CatShelter extends AnimalShelter {
    //does *not* override 
AnimalShelter.putAnimal()
    public void putAnimal(Cat c) {
        System.out.println("CatShelter.putAnimal");
    }
}


class Test {
    public Test(AnimalShelter shelter) {
        shelter.putAnimal(new Dog());
    }
}

{
    //Outputs "
AnimalShelter.putAnimal".
    //Thanks to this behavior of the compiler, it's impossible to put a dog
    //in a cat shelter.
    new Test(new CatShelter());
}

We’ve been discussing how trygve should handle this. I think it matters less what we do than that the philosophy is clear. Different existing languages have different philosophies on this.

First, talking about overloaded functions across an inheritance hierarchy, when trying to encourage object-think instead of class think, is already tempting fate. To start playing with the types of the interfaces to these methods is slapping fate in the face.

To me, putting add(Animal) in the base class and then adding the script add(Cat) in a derived class is tantamount instead putting the script addCat(Cat) in the derived class. Neither one precludes passing a Dog to the add script of the object. To add add(Cat) in a derived class does not constitute overriding in the LSP, and no one has any right to believe it should, though there are likely to be novice programmers that get this wrong.

The trygve system currently just ignores the situation and takes the position that two versions of add like this have no special relationship. The goal I achieve in taking this perspective is that the user can’t get into trouble; i.e. there is no possibility of “message not understood” at run time. The run-time surprise is in the form of not calling  add(Cat) when invoking the script through a base class identifier.

So — from the perspective of reasoning about the instances instead of types — how do you think trygve should handle this?

— Cope

Egon Elbre

unread,
May 13, 2016, 3:40:44 PM5/13/16
to object-composition
Remove inheritance :)

I suspect by not using inheritance there can be novel solutions: e.g.

class AnimalShelter { ... }
specialization CatShelter of AnimalShelter where Animal is Cat { ... }

Where AnimalShelter and CatShelter are invariant and Animal is replaced in CatShelter recursively with Cat.

+ Egon

Rune Funch Søltoft

unread,
May 13, 2016, 4:20:54 PM5/13/16
to object-co...@googlegroups.com
I have a feeling that if we got rid of classes altogether we could achieve all of the design capabilities with contexts and dumb data and no inheritance. That's way over in a possible end game I know, so what would I want for trygve? I would still like to see how far we could take it without inheritance

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 https://groups.google.com/group/object-composition.
For more options, visit https://groups.google.com/d/optout.

Raoul Duke

unread,
May 13, 2016, 4:22:54 PM5/13/16
to object-co...@googlegroups.com
Hasn't there been discussion of how applicative / fp styles are actually better? :-)

Matthew Browne

unread,
May 13, 2016, 4:35:08 PM5/13/16
to object-co...@googlegroups.com
On 5/13/16 4:20 PM, Rune Funch Søltoft wrote:
I have a feeling that if we got rid of classes altogether we could achieve all of the design capabilities with contexts and dumb data and no inheritance. That's way over in a possible end game I know, so what would I want for trygve? I would still like to see how far we could take it without inheritance
I was just going to say something very similar...I still think that it would be better to get rid of classes completely, and provide some mechanism for Context specialization instead. If a Context can be an object in the Data projection (e.g. Account in the money transfer example) then why can't there be a way of specifying Context parent types and sub-types? (Or maybe we consider nested contexts sufficient for that?) While classes obviously cannot do what contexts can do (since they lack roles), contexts can do nearly everything that classes can do, with the notable exception of inheritance. So I agree with what Rune said a while ago - having both contexts and classes is unnecessary (we only really need contexts).

I realize that eliminating classes could have downsides as well. It's obviously a departure from DCI as originally conceived by Trygve. And one of the most important things newcomers need to learn is differentiating between what-the-system-is and what-the-system-does in the code, and "everything is a context" might make that differentiation less clear to beginners. But ultimately, and especially if we aren't tethered to the baggage of class-oriented programming, I think it would be better to add a few capabilities to contexts and get rid of classes.

James O Coplien

unread,
May 14, 2016, 5:52:19 AM5/14/16
to object-co...@googlegroups.com

On 13 May 2016, at 22:22, Raoul Duke <rao...@gmail.com> wrote:

Hasn't there been discussion of how applicative / fp styles are actually better? :-)

That’s actually independent of this discussion.

James O Coplien

unread,
May 14, 2016, 5:53:29 AM5/14/16
to object-co...@googlegroups.com

On 13 May 2016, at 22:35, Matthew Browne <mbro...@gmail.com> wrote:

I was just going to say something very similar...I still think that it would be better to get rid of classes completely, and provide some mechanism for Context specialization instead.

That just solves a different problem (and I like it) m´but it just moves this problem elsewhere. We’d still need to choose between one of the several flavours of Liskov.

Matthew Browne

unread,
May 14, 2016, 7:51:09 AM5/14/16
to object-co...@googlegroups.com
One option I've been thinking about is compiler-assisted forwarding, for example:

context Vehicle {
    private Person owner_;
    private Person driver_;
   
    public Vehicle(Person owner, Person driver) {
        this.owner_ = owner;
        this.driver_ = driver;
    }
   
    public Person owner() {return owner_};
    public void changeOwner(Person newOwner) {
        this.owner_ = newOwner;
    }
   
    public Person driver() {return driver_};
    public void changeDriver(Person newDriver) {
        this.driver_ = newDriver;
    }   
}

context Car embed vehicle {
    constructor(Person owner, Person driver) {
        vehicle = new Vehicle(owner, driver);
    }
    public void changeOwner(Person newOwner) {
        vehicle.changeOwner(newOwner);
        System.out.println(newOwner.name() + " just bought or received a car!");
    }
    public void test(Person p) {
        //Within the Context, the forwarding methods are unavailable, in order to help readability.
        //So this is allowed:
        vehicle.owner();
        //But this is not:
        owner();   //compile error
    }
    public int numWheels() {
        return 4;
    }
}

{
    Person fred = new Person("Fred");
    Person sally = new Person("Sally");
    Person sue = new Person("Sue");
    Car car = new Car(fred, sally);
    car.changeOwner(sue);
   
    //Can be called in same way as changeOwner() although we didn't explicitly write a
    //Car.changeDriver() method, because forwarding methods are created automatically for public methods
    //of embedded objects.
    car.changeDriver(fred);
}


Obviously there would need to be proper handling of name clashes when embedding more than one object (there could be syntax for specifying which take precedence, similarly to how traits handle name clashes in some other languages).

I'd also be interested in hearing more about Egon's idea:

James O. Coplien

unread,
May 14, 2016, 8:18:41 AM5/14/16
to object-co...@googlegroups.com

On 13 May 2016, at 21:40, Egon Elbre <egon...@gmail.com> wrote:

Remove inheritance :)

I suspect by not using inheritance there can be novel solutions: e.g.

class AnimalShelter { ... }
specialization CatShelter of AnimalShelter where Animal is Cat { ... }

Where AnimalShelter and CatShelter are invariant and Animal is replaced in CatShelter recursively with Cat.

I think this does literally what you suggest:

class Cat {}
class Dog {}

class AnimalShelter <Animal> {
    public void putAnimal(Animal a) {
        System.out.println("AnimalShelter.putAnimal")
    }
}

class Test {
    public Test(AnimalShelter<Dog> shelter) {
        shelter.putAnimal(new Dog())
    }
}

new Test(new AnimalShelter<Cat>())

James O. Coplien

unread,
May 14, 2016, 8:23:12 AM5/14/16
to object-co...@googlegroups.com

> On 13 May 2016, at 21:40, Egon Elbre <egon...@gmail.com> wrote:
>
> Remove inheritance :)
>
> I suspect by not using inheritance there can be novel solutions: e.g.
>
> class AnimalShelter { ... }
> specialization CatShelter of AnimalShelter where Animal is Cat { ... }
>
> Where AnimalShelter and CatShelter are invariant and Animal is replaced in CatShelter recursively with Cat.

Is the stuff in { … } allowed to override the stuff in AnimalShelter?

Epicycles...

Egon Elbre

unread,
May 14, 2016, 9:11:57 AM5/14/16
to object-composition
Nope, I was also considering completely removing { ... } part for the specialization.

Egon Elbre

unread,
May 14, 2016, 9:18:01 AM5/14/16
to object-composition
Yes and no. In there, Animal is a type parameter not an interface. Cat/Dog have no relation between them. And usually AnimalShelter<Dog> and AnimalShelter<Cat> are either covariant or contravariant.

Egon Elbre

unread,
May 14, 2016, 9:20:59 AM5/14/16
to object-composition


On Saturday, 14 May 2016 14:51:09 UTC+3, Matthew Browne wrote:
On 5/14/16 5:53 AM, James O Coplien wrote:

On 13 May 2016, at 22:35, Matthew Browne <mbro...@gmail.com> wrote:

I was just going to say something very similar...I still think that it would be better to get rid of classes completely, and provide some mechanism for Context specialization instead.

That just solves a different problem (and I like it) m´but it just moves this problem elsewhere. We’d still need to choose between one of the several flavours of Liskov.
One option I've been thinking about is compiler-assisted forwarding, for example:

Take a look at Go... https://golang.org/ref/spec#Struct_types search for embedding. It's quite nice to use.

Matthew Browne

unread,
May 14, 2016, 9:34:29 AM5/14/16
to object-co...@googlegroups.com
The Go language was actually one of my inspirations for this. But I wanted to also have a way to call embedded methods directly, for convenience, hence the automatic forwarding.
--
Sent from my Android device with K-9 Mail.

Egon Elbre

unread,
May 14, 2016, 9:38:49 AM5/14/16
to object-composition
On Saturday, 14 May 2016 16:34:29 UTC+3, Matthew Browne wrote:
The Go language was actually one of my inspirations for this. But I wanted to also have a way to call embedded methods directly, for convenience, hence the automatic forwarding.

James O Coplien

unread,
May 14, 2016, 10:19:06 AM5/14/16
to object-co...@googlegroups.com
On 14 May 2016, at 15:18, Egon Elbre <egon...@gmail.com> wrote:

Yes and no. In there, Animal is a type parameter not an interface.

So? There’s no semantic difference: only syntactic saccharin.


Cat/Dog have no relation between them.

Nor do they in your example. Or do they? If they do, how do you express it, if not by inheritance?


And usually AnimalShelter<Dog> and AnimalShelter<Cat> are either covariant or contravariant.

Covariant and contravariant… in what? They are two different classes. There is no property of any language I know that would guarantee any type relationship between two instances of a parameterized type. C++ is kind of at the extreme where the arguments are little more than macros.

And in any case, this analysis takes us far from thinking about networks of objects, back into the gutter of class-oriented thinking. When I put these considerations on the table I was hoping for more insight along the lines of whether we can reason about the objects. Removing inheritance is a suggestion in the right ballpark, but replacing it by this is a step in the wrong direction.

James O Coplien

unread,
May 14, 2016, 10:20:56 AM5/14/16
to object-co...@googlegroups.com
If you’re going to go that far, you might as well go for full delegation. Forwarding makes code unreadable, and I guess delegation does, too. At least with delegation you have the type system working for you. But, of course, by the Treaty of Orlando, its semantics are the same of those of subtyping inheritance, which means we buy back all those problems…

Egon Elbre

unread,
May 14, 2016, 10:41:28 AM5/14/16
to object-composition

On Saturday, 14 May 2016 17:19:06 UTC+3, cope wrote:

On 14 May 2016, at 15:18, Egon Elbre <egon...@gmail.com> wrote:

Yes and no. In there, Animal is a type parameter not an interface.

So? There’s no semantic difference: only syntactic saccharin.

In one case you have full realized code in the other case you have partial code.

When there is no type-specialization, I can create an AnimalShelter object (without the class) and specialize that object for a particular Animal. However I do not know how to have the same behavior with a type parameter.
Cat/Dog have no relation between them.
Nor do they in your example. Or do they? If they do, how do you express it, if not by inheritance?

By implementing an interface, tagging or constraints.
And usually AnimalShelter<Dog> and AnimalShelter<Cat> are either covariant or contravariant.
Covariant and contravariant… in what? They are two different classes. There is no property of any language I know that would guarantee any type relationship between two instances of a parameterized type. C++ is kind of at the extreme where the arguments are little more than macros.

What I meant was (I know I had a mistake in my writing.)

void process(AnimalShelter<Animal> shelter) { ... }

Here I could potentially use either AnimalShelter<Dog> and AnimalShelter<Cat>, alternatively:

void process(AnimalShelter<Dog> shelter) { ... }

Could allow using an AnimalShelter<Animal>.

I would consider AnimalShelter<Animal>, AnimalShelter<Dog>, AnimalShelter<Cat> all distinct types and wouldn't allow writing void process(AnimalShelter<Animal> shelter) { ... }.

Andreas Söderlund

unread,
May 14, 2016, 10:50:00 AM5/14/16
to object-co...@googlegroups.com
When we start talking about specialization and forwarding, it seems that we're getting more into Role territory? There are similarities between a free-floating Role with all fields forwarded, and inherited classes. Just add some state and mix well?

And as Cope mentioned, there's no real difference in using "addCat" instead of "add", except that the structure-loving nerd inside us will feel hurt, of course. :) We don't want to put animals in a CatShelter, we want to put cats there.

So there are naming issues again, and a need to forward fields... what's the real problem here, overloading and overriding? In an attempt to put things in the right ballpark, how about disallowing them, if removing inheritance altogether is too much? Trygve issues a warning for that case for Roles, so it shouldn't be that far off...?

Egon Elbre

unread,
May 14, 2016, 10:50:07 AM5/14/16
to object-composition
On Saturday, 14 May 2016 17:20:56 UTC+3, cope wrote:
If you’re going to go that far, you might as well go for full delegation. Forwarding makes code unreadable, and I guess delegation does, too.

Based on my experience code with delegation is harder to follow and understand than an approach with forwarding. Forwarding retains object boundaries whereas delegation does not. With forwarding, it's much clearer what method will be called and what is in scope.

Forwarding is syntactic sugar for me nothing else.

James O Coplien

unread,
May 14, 2016, 10:51:20 AM5/14/16
to object-co...@googlegroups.com

On 14 May 2016, at 16:41, Egon Elbre <egon...@gmail.com> wrote:

By implementing an interface, tagging or constraints.

This was the original issue I wanted discussed. So you would rather express the relationship between classes through a common interface, or through tagging, or constraints? Do we have a good analysis of why?

This, I think, gets to the heart of the matter, while the example you posited only pushes it somewhere else.

James O Coplien

unread,
May 14, 2016, 10:52:30 AM5/14/16
to object-co...@googlegroups.com
On 14 May 2016, at 16:41, Egon Elbre <egon...@gmail.com> wrote:

However I do not know how to have the same behavior with a type parameter.

Sure — type argument defaulting. I know C++ has it; I don’t know about other languages. The trygve language does not have it because I want to defocus from thinking about classes.

James O Coplien

unread,
May 14, 2016, 10:54:18 AM5/14/16
to object-co...@googlegroups.com

On 14 May 2016, at 16:49, Andreas Söderlund <cisc...@gmail.com> wrote:

And as Cope mentioned, there's no real difference in using "addCat" instead of "add", except that the structure-loving nerd inside us will feel hurt, of course. :) We don't want to put animals in a CatShelter, we want to put cats there.


I’m laughing out loud...

James O Coplien

unread,
May 14, 2016, 10:55:17 AM5/14/16
to object-co...@googlegroups.com

On 14 May 2016, at 16:50, Egon Elbre <egon...@gmail.com> wrote:

Based on my experience code with delegation 

In what language?

Egon Elbre

unread,
May 14, 2016, 11:00:10 AM5/14/16
to object-composition


On Saturday, 14 May 2016 17:51:20 UTC+3, cope wrote:

On 14 May 2016, at 16:41, Egon Elbre <egon...@gmail.com> wrote:

By implementing an interface, tagging or constraints.

This was the original issue I wanted discussed. So you would rather express the relationship between classes through a common interface, or through tagging, or constraints? Do we have a good analysis of why?

Implicit interfaces has grown on me. There are potential errors that can happen with them, however in practice happen rarely. Very light-weight on the syntax. Also allows to properly keep the interface and the type at a distance when necessary reducing the amount of dependencies.

Explicit interfaces are verbose version. I dislike the hard dependency on the interface -- i.e. means that you sometimes have to include code that isn't being used.

Tagging is an interesting approach, however probably suits more Games where you need to process certain entities in a world. I.e. what is collidable and what is not.

Constraints would be interesting to explore, not sure what it would exactly look like. Maybe like "local interface" with the possibility to require public fields.

James O Coplien

unread,
May 14, 2016, 11:04:29 AM5/14/16
to object-co...@googlegroups.com

On 14 May 2016, at 17:00, Egon Elbre <egon...@gmail.com> wrote:

Implicit interfaces has grown on me. There are potential errors that can happen with them, however in practice happen rarely. Very light-weight on the syntax. Also allows to properly keep the interface and the type at a distance when necessary reducing the amount of dependencies.

Explicit interfaces are verbose version. I dislike the hard dependency on the interface -- i.e. means that you sometimes have to include code that isn't being used.

Early research by Perry shows that interfaces and a strong type system correlate to low bug density. I’m going with the numbers here. I’m also going with my experience trying to decipher Trygve’s front-loading code, which invoked instance methods other than those advertised in any any interface associated with the invoking code. It definitely contributed to the code’s obfuscation of the execution sequencing.

Egon Elbre

unread,
May 14, 2016, 11:04:37 AM5/14/16
to object-composition
Mainly JavaScript.
Delphi, Java, C# suffer similar problems when a parent class calls an overridden method.

Egon Elbre

unread,
May 14, 2016, 11:10:38 AM5/14/16
to object-composition


On Saturday, 14 May 2016 18:04:29 UTC+3, cope wrote:

On 14 May 2016, at 17:00, Egon Elbre <egon...@gmail.com> wrote:

Implicit interfaces has grown on me. There are potential errors that can happen with them, however in practice happen rarely. Very light-weight on the syntax. Also allows to properly keep the interface and the type at a distance when necessary reducing the amount of dependencies.

Explicit interfaces are verbose version. I dislike the hard dependency on the interface -- i.e. means that you sometimes have to include code that isn't being used.

Early research by Perry shows that interfaces and a strong type system correlate to low bug density.

Aah, by implicit interfaces I mean the approach that Go uses...  (https://play.golang.org/p/dReJhXKt9b press run to see the error message)

I'm all for a strong type system. I would love to see an OO language with a strong type system, without classes.

James O Coplien

unread,
May 14, 2016, 12:32:56 PM5/14/16
to object-co...@googlegroups.com

On 14 May 2016, at 17:10, Egon Elbre <egon...@gmail.com> wrote:

Aah, by implicit interfaces I mean the approach that Go uses...  (https://play.golang.org/p/dReJhXKt9b press run to see the error message)

This doesn’t seem any different from the ways roles are bound to instances in trygve. It uses the same signature-comparison mechanism because there is never any a priori relationship between a Role type and a class or interface type.

There’s really nothing “implicit” about it...

Trygve Reenskaug

unread,
May 14, 2016, 2:38:31 PM5/14/16
to object-co...@googlegroups.com

I fail to see your problem.  There are 5 roles in BB4bPlan, 2 of them with methods. (The frontloading algorithm in the big planning example is similar and well separated from the rest of the program.)

No
        diagram

The ACTIVITY role communicates with its predecessors in its only role methods:

frontloadFrom: projectStartWeek
    | maxPred |
    self earlyStart: projectStartWeek.
    maxPred := PREDECESSORS detectMax: [:pred | pred earlyFinish].
    maxPred ifNotNil: [self earlyStart: maxPred earlyFinish + 1].

Many people hate the syntax, but I fail to see that the algorithm itself can be confusing.

The single MODEL role method is equally simple

frontloadFrom: projectStartWeek 
    ALLACTIVITIES do: [:act | act resetForFrontload].
    [CONTEXT remap. ACTIVITY notNil]
    whileTrue:
        [ACTIVITY frontloadFrom: projectStartWeek].

The CONTEXT remap method for the ACTIVITY role controls the loop:

ACTIVITY
    ^data allActivities
        detect:
            [:act |
            act earlyStart isNil
            and:
                [(data predecessorsOf: act) noneSatisfy: [:pred | pred earlyFinish isNil]]]
        ifNone: [nil]

Where's your problem?

There are, of course, no interface specifications in Squeak; we rely on understandable method names and trust that they don't lie. AlanKay: "Questions of concrete representation can thus be postponed almost indefinitely because we are mainly concerned that the computers behave appropriately, and are interested in particular strategies only if the results are off or come back too slowly."

Matthew Browne

unread,
May 14, 2016, 2:56:13 PM5/14/16
to object-co...@googlegroups.com
On 5/14/16 10:49 AM, Andreas Söderlund wrote:
So there are naming issues again, and a need to forward fields... what's the real problem here, overloading and overriding? In an attempt to put things in the right ballpark, how about disallowing them, if removing inheritance altogether is too much? Trygve issues a warning for that case for Roles, so it shouldn't be that far off...?
If we and/or Cope decide to keep inheritance in trygve for whatever reason, then I think prohibiting overriding altogether would be going too far. But I do think that examples like the animal shelter code I posted should be illegal, because the potential for confusion is too high to justify the rare situation where something like that might be useful. (BTW the example came from Wikipedia and was an example of how Eiffel supports covariant parameter types and how that's problematic...in trygve it's not treated as an override, but I think it would look like an override to many programmers, including myself, coming from a background mainly in dynamically-typed languages.)

I think overriding with the same type should definitely be allowed, otherwise it would be rather limiting:

class FelineExhibit {
    public void add(Feline f) {}
}

class CatExhibit extends
FelineExhibit {
    public void add(Feline f) {}
}


And maybe contravariant parameter types could be allowed too (but this already starts to make the code a bit harder to follow, so maybe not):

class
FelineExhibit {
    public void add(Animal a) {}
}


But I think the following should be forbidden, because although in trygve it doesn't actually permit covariant parameter types, it looks like that's the intent and it's just confusing:

class FelineExhibit {
    public void add(Feline f) {}
}

class CatExhibit
{
    public void add(Cat a) {}
}


But if we can find a good alternative to classes and inheritance that doesn't introduce new problems, I'm all for it.

Matthew Browne

unread,
May 14, 2016, 3:11:13 PM5/14/16
to object-co...@googlegroups.com
On 5/14/16 2:56 PM, Matthew Browne wrote:
And maybe contravariant parameter types could be allowed too (but this already starts to make the code a bit harder to follow, so maybe not):

class
FelineExhibit {
    public void add(Animal a) {}
}

I didn't get that one right. What I meant to demonstrate was this:

class AnimalExhibit {
    public void addFeline(Feline f) {}

    ...
}

class FelineExhibit extends AnimalExhibit {
    public void
addFeline(Animal a) {}
}


(Not a good example of why you'd want a contravariant parameter type, and a bad design, but at least it's easy to compare with the other examples.)

James O Coplien

unread,
May 14, 2016, 3:33:31 PM5/14/16
to object-co...@googlegroups.com

On 14 May 2016, at 20:38, Trygve Reenskaug <try...@ifi.uio.no> wrote:

I fail to see your problem.

I don’t have time to look into it right now; it would take me a day or two to find the code again and to get back into it enough (it is not a book-sized example, but more like an example that is the size of a small book) to figure out enough to be able to reproduce my confusion. But based on some notes from the translation I was doing, as a simple example, I’ve noted that you send a size message to Predecessors. A get message to Predecessors. And on Activities. There were, rough order of magnitude, dozens more.

Andreas Söderlund

unread,
May 15, 2016, 7:28:30 AM5/15/16
to object-co...@googlegroups.com
lör 14 maj 2016 kl 20:56 skrev Matthew Browne <mbro...@gmail.com>:

I think overriding with the same type should definitely be allowed, otherwise it would be rather limiting:

class FelineExhibit {
    public void add(Feline f) {}
}

class CatExhibit extends
FelineExhibit {
    public void add(Feline f) {}
}

Theoretical examples can surely be found and used as an argument (haven't seen that many practical ones though). I'm sure one could argue for almost any solution here, given how many solutions exists already. 

My reasoning is simple: What has been a principal line of thought in DCI all the time, expressed like this in the first headline of the Artima article:

"Objects are principally about people and their mental models—not polymorphism, coupling and cohesion"

So why can't inheritance be made simple? I think this description from Lean Architecture is great: "Abstract base classes (ABCs) represent an interesting paradox. They are real source code but generate no object code on their own. They guide system form but contribute nothing to the product footprint. Their main function is to guide programmers as they weave use case code into the product."

That's how I'd like to, and how I have viewed inheritance for the past years. "Fill in the blanks", kind of. It's something 7-year olds will understand too.

But maybe I'm building too simple things. If reality needs co/contravariance and polymorphism, what can I say. Wish we had the source code for the Universe for comparison... :)

But if we can find a good alternative to classes and inheritance that doesn't introduce new problems, I'm all for it.

Me too, for sure.

Trygve Reenskaug

unread,
May 15, 2016, 8:21:07 AM5/15/16
to object-co...@googlegroups.com

Cope, I continue this discussion because you seem to build some of the Trygve syntax on your bad experience with myfrontloading code. This experience leads to what I think is an overly bureaucratic Trygve.

I have searched all users of size and can't find any related to frontloading in the small example, BB4bPlan, or in the big one, BB9Planning.  You have apparently got lost in some source code that is the size of a small book; it must be BB9Planning. Here, the frontloading algorithm is specified with 64 LOC, see below.  Comments are shown in blue, source code in black.

-------------------------------------------------------

projection: Context-Model

Class: FrontloadCtx

Context subclass: #FrontloadCtx
    instanceVariableNames: 'activity model'
    category: 'Planning-Context-Model'

" Find earliest time period for each activity. "

There are 5 roles that are connected as shown. 2 are shown with heavy outline because they are methodful. The interaction starts in MODEL, this is indicated with a green shadow.

No
          diagram

instanceMethods: role binding

remap
    " Need to find an activity that is ready for planning first because it is used by two role mappings. "
    activity := model allActivities 


        detect: 
            [:act | 
            act earlyStart isNil and: 

                [(model predecessorsOf: act) noneSatisfy: [:pred | pred earlyFinish isNil]]] 
        ifNone: [nil].
    super remap.

ACTIVITY
    ^activity

CONTEXT
    ^self

MODEL
    ^model

PREDECESSORS
    ^activity 
        ifNil: [Array new]
        ifNotNil: [model predecessorsOf: activity]

PROJECTSTART
    ^model projectStart

instanceMethods: operations

frontload: mod
    model := mod.
    self triggerInteractionFrom: #MODEL with: #frontload.

roleMethods: MODEL.

frontload 
    MODEL allActivities do: [:act | act earlyStart: nil]. " set to unplanned "
    [CONTEXT remap. ACTIVITY notNil] whileTrue: [ACTIVITY frontload].

roleMethods: ACTIVITY

frontload 
    | maxPred |


    maxPred := PREDECESSORS detectMax: [:pred | pred earlyFinish].
    maxPred 

        ifNil: [ACTIVITY earlyStart: PROJECTSTART.] 
        ifNotNil: [ACTIVITY earlyStart: maxPred earlyFinish + 1].

-------------------------------------------------

That's all. There is, of course, the problem that this code is as unreadable to most people as C++ is to me. This makes it hard for them to form a reasoned opinion. But that doesn't mean that it is unreadable to someone who is familiar with the language. I suspect you got lost in the part of the class library that is part of the ST language. In your Trygve write-up you describe the for-statement:
         for (int aScore : scores) total = total + aScore
In ST, this is written:
        scores do: [aScore | total := total + aScore]
This construct is explained in a beginner's guide to ST. A major difference is that while the first construct is part of the Trygve language, the second is part of the ST class library.  It is, therefore, possible to look up the code in ST (and not in Trygve, where you have to use the construct on trust):

Array::do: aBlock
    1 to: self size do:
        [:index | aBlock value: (self at: index)]

could this be where you found your confusing size? The language parts of the class library are indistinguishable from the classes defined by the user. This is unconventional and can confuse the innocent.

--

James O Coplien

unread,
May 15, 2016, 9:09:17 AM5/15/16
to object-co...@googlegroups.com
Beautifully said.

What’s more, to make your point, I notice that the object perspective becomes lost whenever we delve into a new thread on one of these polymorphism topics.


James O Coplien

unread,
May 15, 2016, 9:14:38 AM5/15/16
to object-co...@googlegroups.com

On 15 May 2016, at 14:21, Trygve Reenskaug <try...@ifi.uio.no> wrote:

Cope, I continue this discussion because you seem to build some of the Trygve syntax on your bad experience with myfrontloading code. This experience leads to what I think is an overly bureaucratic Trygve.

You once said that you thought the requires section was a good idea. Many others I’ve talked to at the conferences and other fora I’ve recently visited agree.

What else is it about trygve that you find overly bureaucratic? And do you have a suggestion for a better alternative? And why it’s better?

(Other than saying: “Let’s just go back to Squeak”, that is?)

Matthew Browne

unread,
May 15, 2016, 9:20:28 AM5/15/16
to object-co...@googlegroups.com

I strongly suspect that if the Squeak implementation had a syntax for specifying role-object contracts, Cope and others would have found the code significantly easier to read, since it would have shown clearly which instance methods were being used in each Context. The trygve language goes another step beyond that by not automatically exposing methods in the contract to other roles - perhaps that's what Trygve is referring to, and I have some doubts about the necessity of that as well. But if it helps some people to find the code easier to reason about without getting in the way for others, then maybe it's not "overly bureaucratic".

James O Coplien

unread,
May 15, 2016, 10:03:19 AM5/15/16
to object-co...@googlegroups.com

On 15 May 2016, at 15:20, Matthew Browne <mbro...@gmail.com> wrote:

The trygve language goes another step beyond that by not automatically exposing methods in the contract to other roles - perhaps that's what Trygve is referring to

We should probably let Trygve say what his concerns are before responding to them. But if this is Matthew’s concern, I can counter by saying that the total lack of any declaration of the Role player’s instance methods leaves the code in a state where it knows much less than the coder pretends to know, and that kind of goes against the prime directive of code legibility. 

This community talked much about the Role / object contract and there were a few wishes to make it concrete; trygve does that.

To overload that contract as an implied contract for other Roles takes away the Role’s control over its Role-player’s state, because it cannot know if other callers are modifying the Role-player’s state without it’s “knowledge.” It seems computationally equivalent to letting a Role-player play multiple Roles. In any case, it means that there is state shared between multiple Roles (there is potentially direct access to Role-player state without being moderated by any method of the Role it is playing). It is in some ways the dual of allowing Roles to have state.

It’s important — from the perspective of being able to reason about the code — that only the Role be able to change its Role-player’s state. Without that restriction it is impossible to reason about object state from a Role, and without that it is impossible to reason about system state of a Context.

It seems that Trygve had to pull out his diagrams to try to clarify these relationships to me. First, unfortunately, the diagrams are at the wrong granularity to make explicit the tacit knowledge he holds about the Role-players’ instance methods. None of these diagrams alone address any of my specific queries. Second, that one needs an environment and notation to understand what’s going on shows a failure on the part of the code to express itself legibly. If the claim is to be that DCI will be useful only together with a graphical environment, we can either have someone commit to making that environment or, more likely, we can all stop right now. No programming approach other than Excel has ever succeeded on the merits of its environment over its textual language. The road is littered with the corpses of systems that stood on the power of environments, and I’m not about to shout back at history louder than it has shouted this to us. Graphical environments almost always fall in the traps of unportability and lack of expressiveness. Not only am I not smart enough to overcome those problems, but I don’t know anyone who is.

Trygve Reenskaug

unread,
May 15, 2016, 11:44:49 AM5/15/16
to object-co...@googlegroups.com
I think the requires section is a very good innovation. I see it as part of the role mapping function because it lists the part of the roleplayer's interface that is required in the current context.

I think the following NONCOMPLAINT reflects something overly bureaucratic in Trygve because the messages a role can receive from other roles is made a subset of the messages it can receive from itself.
line 239: NONCOMPLIANT: Trying to enact object script `setPosition(int)' without using the interface of the Role it is playing: `SpellChecker’.
I think Trygve would be better without this distinction because it would be simpler: A role would be the name of an object. Full stop. Simplicity is always my goal and it's always a question of balance between power and simplicity. In this case, the two interfaces adds complexity without an indispensable gain, IMO.

I believe you once gave your problem with reading my frontloading context as one of your reasons for wanting the distinction. So it would be interesting to identify the reason for your confusion.

P.S. I wish it were possible to discuss DCI issues without you implying that Squeak is the only thought in my head.

James O Coplien

unread,
May 15, 2016, 12:37:48 PM5/15/16
to object-co...@googlegroups.com
How are these two programs different?

class Int {
public Int(int i) { i_ = i.clone }
public Int(int i, Int otherOne) { i_ = i.clone other = otherOne }
public void incr() { i_ = i_ + 1; if (other != null) other.incr }
public void print() const { System.out.println(i_) }
   int i_;
   Int other
}

context UseCase {
role I1 {
public void step() { incr() }
public void report() const { this.print() }
} requires {
void print() const;
void incr()
}
   role I2 {
public void step() { incr() }
public void report() const { this.print() }
} requires {
void print() const;
void incr()
}
public void trigger(Int i1, Int i2) {
I1 = i1
I2 = i2
I1.step
I1.report
I2.report
}
}

{
Int i2 = new Int(3);
Int i1 = new Int(4, i2);
new UseCase().trigger(i1, i2)
}

--------

class Int {
public Int(int i) { i_ = i.clone }
public void incr() { i_ = i_ + 1}
public void print() const { System.out.println(i_) }
   int i_;
}

context UseCase {
role I1 {
public void step() { incr(); I2.incr() }
public void report() const { this.print() }
} requires {
void print() const;
void incr()
}
   role I2 {
public void step() { incr() }
public void report() const { this.print() }
} requires {
void print() const;
void incr()
}
public void trigger(Int i1, Int i2) {
I1 = i1
I2 = i2
I1.step
I1.report
I2.report
}
}

{
Int i2 = new Int(3);
Int i1 = new Int(4);
new UseCase().trigger(i1, i2)
}

Matthew Browne

unread,
May 15, 2016, 7:47:33 PM5/15/16
to object-co...@googlegroups.com
On 5/15/16 10:03 AM, James O Coplien wrote:
> It’s important — from the perspective of being able to reason about
> the code — that only the Role be able to change its Role-player’s
> state. Without that restriction it is impossible to reason about
> object state from a Role,
"Impossible" is a pretty strong word to use.
> and without that it is impossible to reason about system state of a
> Context.
All the methods that can be called anywhere in the Context are listed in
either the role-object contract (in the case of instance methods) or in
the role itself (in the case of role methods). So while it may be true
that it's harder to reason about the state of a role player when you
have to look outside the role to do so, you certainly shouldn't have to
look outside the Context to do so. So I don't think your second
assertion follows from the first. I don't see how promoting an instance
method to a role method (as was recently introduced to trygve) or
writing your own forwarding method avoids the need to reason about the
state of multiple interacting objects. Roles can still affect the state
of other role players; it's just a question of whether they have to go
through the gateway of the role interface in order to do so. I think the
'const' modifier already helps a good bit in this area.

The other thing that bothers me a bit about prohibiting direct access to
instance methods from other roles is that it maintains what I see as
essentially a compile-time separation at run-time. A major tenet of DCI
is of course to merge data and behavior at run-time so that your code is
communicating with a single unified object instead of two separate
entities. From that perspective it's kind of surprising and perhaps
makes it harder to understand DCI if you see a method listed in a
role-object contract and you try to call it from another role and it
doesn't work. On the other hand we are all accustomed to access
restrictions, and it makes sense from that perspective.

So in summary, I certainly agree with Trygve that the current
restrictions in trygve about this are not "indispensable" to reasoning
about the code. However, I do question his implication that because it's
more complex and not indispensable, this idea should necessarily be
rejected. If it helps Cope and others to reason about the code better,
that's an important consideration in favor of it. And that needs to be
weighed against the potential detriment to others due to the added
complexity.

Trygve Reenskaug

unread,
May 16, 2016, 4:36:23 AM5/16/16
to object-co...@googlegroups.com

Example: Frontloading. It is essential that all activities are in a known state before the computation of the activities' earlyStart starts. Hence the very first expression in the interaction algorithm:

ST:
    PLAN allActivities do: [:act | act earlyStart: nil].
" set to unplanned "
Trygve:
    for (Activity act : model.allActivities()) act.setEarlyStart(void)
// set to unplanned
Original Java etc:
    Activity[] allActivities = model.allActivities();

    for (int i = 0; i < allActivities.size(); i++) {
        allActivities[i].setEarlyStart(void) // set to unplanned
    }

James O Coplien

unread,
May 17, 2016, 4:54:37 PM5/17/16
to object-co...@googlegroups.com

On 16 May 2016, at 01:47, Matthew Browne <mbro...@gmail.com> wrote:

On 5/15/16 10:03 AM, James O Coplien wrote:
It’s important — from the perspective of being able to reason about the code — that only the Role be able to change its Role-player’s state. Without that restriction it is impossible to reason about object state from a Role,
"Impossible" is a pretty strong word to use.

Words mean things. I mean impossible here. It reduces to the halting problem. It’s provable. Quod erat demonstrandum.


and without that it is impossible to reason about system state of a Context.
All the methods that can be called anywhere in the Context are listed in either the role-object contract (in the case of instance methods) or in the role itself (in the case of role methods). So while it may be true that it's harder to reason about the state of a role player when you have to look outside the role to do so, you certainly shouldn't have to look outside the Context to do so. So I don't think your second assertion follows from the first. I don't see how promoting an instance method to a role method (as was recently introduced to trygve) or writing your own forward…

First, I might agree with you if such access were fatal. It is a warning. If you believe that warnings have a place in compilers, then if ever there was a place for a warning, this is it. It is possible that you find the warning annoying because you know what you are doing. The usual way to communicate that in Java is with a @SuppressWarnings; in other curly-braced languages it’s often a #pragma. In trygve the mechanism is to declare the signature in the interface part of the Role.

I could make the warning fatal (I indeed threatened to do that) but because this is an exploratory effort, some variance on perspective can be tolerated, so it’s not currently fatal. This gives the perspective that the whole argument is about not wanting to see warnings when I write my code. Warnings typically ask programmers to have a second look at the code to ensure they meant what they said. In a good language, additional tweaking can make the warning go away. This is common compiler technology practice.This issue does not fall in the heart of research into the DCI computational model. Because it is a warning, I tend to dismiss the importance of suggestions to change the language. (I do still think there is a good discussion to be had here about the computational model and the language has done its job to elicit it. I am happy to continue the discussion, but on balance I think the risk of removing the warning is greater than the programmer-fixable inconvenience of leaving it in. So with respect to removing it from the language, I dismiss this argument.)

And I won’t imminently turn the warning into a fatal error.

Why is it a warning in the first place? Good code has a property called intentionality, which means that it reveals the designer’s intent. The intent being expressed by the requires block is to expose instance methods for use by the Role methods. Not having this in Ruby was a real nuisance, and while it was nice in the C++ implementation of DCI, it was a bit awkward because it depended on the CRTP. The requires section is valuable and no one seems to disagree with it:

So it went into trygve from the beginning. I think it’s a good thing in its own right. We have often discussed it here on the list. We have talked about making it explicit. I don’t think anyone else has done that (except Matthew in his new language variant). I have.

To take Trygve’s suggestion would be to change the semantics of what requires means. That’s a problem which even Trygve seems to acknowledge:

On 08 May 2016, at 10:52, Trygve Reenskaug <try...@ifi.uio.no> wrote:

Here's an idea. Gain: the complete role interface  distinguishing which parts are visible to the environment. Lose: The explicit role/object interface.

With the proposed change (promiscuous access to scripts declared in the requires section), the requires  clause becomes a contract between the Role-player and the Context instead of the Role-player and the Role. If a single instance plays multiple Roles in a single Context (not uncommon) then it is likely that a given declaration of how that instance interacts with the Context will appear in two places (according to the compile-time needs of the more class-like Role) than of the instance. But, worse, in the more common case of each instance playing a single Role, it means that the Role no longer has authority over access to the object. The Role encapsulates the object in methods whose preconditions and postconditions we can deduce from the code by reading it.

Trygve wants to emphasize the place of the Roles in being names for objects:

On 15 May 2016, at 17:44, Trygve Reenskaug <try...@ifi.uio.no> wrote:

I think Trygve would be better without this distinction because it would be simpler: A role would be the name of an object. Full stop.

That is certainly a style of programming, but I wouldn’t call it DCI in terms of the pictures Trygve has drawn, the code he has written, and the discussions we have had here. The following code meets all the criteria people have been asking for but I hardly feel it rises to provide DCI’s benefits. It just makes identifiers into fat first-class administrative entities (talk about bureaucratic!)

context Arena implements EventHandler {
   public Arena() {
 MyPanel panel = new MyPanel();
 THE_PANEL = panel;
 THE_PANEL.clear();
 BALL = new BallObject(50, 50);
 PADDLE = new Point(450, 560);
 panel.setEventHandler(this)
   }
   public void run() {
 do {
THE_PANEL.clear();
PADDLE.draw();
BALL.draw()
BALL.velocityAdjust();
BALL.step();
THE_PANEL.flush();
Thread.sleep(20)
 } while (true)
   }


   role THE_PANEL {
 
   } requires {
void drawCircle(int x, int y, int r);
void drawPADDLE(int xs, int ys, int h, int w);
int maxX() const;
int maxY() const;
void flush();
void clear();
void fillOval(int x, int y, int h, int w);
void drawRect(int x, int y, int h, int w);
void fillRect(int x, int y, int h, int w);
int xsize() const;
int ysize() const;
void setColor(Color color);
void repaint();
   }

   role PADDLE {
 
   } requires {
int thickness() const;
int width() const;
void draw();
void erase();
void moveTo(Point p),
boolean contains(int x);
int vertical() const { return y() - thickness() }
void moveTo(Point p);
boolean contains(int x)
int vertical() const;
void setXY(int x, int y);
int x() const;
int y() const
   }
   role BALL {

   } requires {
void draw();
void erase();
void step();
void velocityAdjust()
boolean bouncingOffOfPADDLE() 
void setXY(int x, int y);
int x() const;
int y() const;
Point velocity();
void setVelocity(Point velocity);
int radius()
   }

   private boolean handleEvent(Event e) {
if (e.id == Event.MOUSE_MOVE) {
PADDLE.moveTo(new Point(e.x, e.y))
}
return true
   }
}

new Arena().run()

This code would compile and run if all the code on the other side of the trusted abstraction boundary were there in the classes. Because it is on the other side of the trusted abstraction boundary, that you are unable to read it shouldn’t make you nervous. It meets all the criteria posited by those advocating direct access of instance methods. Can you analyze the code to understand what it does? Good, if you can, because then we don’t need DCI and we can just go back to class-oriented programming. If you can’t, it just may be the fault of the reasoning that has been presented in recent mails. Point finale.

Matthew writes:

On 16 May 2016, at 01:47, Matthew Browne <mbro...@gmail.com> wrote:

Roles can still affect the state of other role players; it's just a question of whether they have to go through the gateway of the role interface in order to do so. I think the 'const' modifier already helps a good bit in this area.

Trygve and I have talked much about the place of abstraction boundaries, and Trygve has been the only person able to convince me that there is a place for abstraction (trustful discarding of detailed concern) and that it lies exactly at these boundaries. The major abstraction boundary in DCI isn’t the object: it is the Role / instance interface. I don’t take a Role’s functionality on trust, which is why it’s code is accessible to any reader. I do need to take it on faith that what happens on the other side of the requires contract because that code isn’t accessible.

To do that means that the class has been designed with integrity. Objects are units of maintaining their own state integrity. I should be able to ask an array object its length and, if its length is greater than 0, I should be able to then access or even extract a member of that array. An object helps assure this integrity by associating the data with the methods that directly change their value. This was Constantine’s original definition of cohesion (together with the fact that these methods tended to be more connected to each other than to other methods). Its dual is coupling. So we can treat a dumb object as a trusted entity.

Now we recur on that concept and add new logic to the object, infused from its Role. We now again want to reason about the integrity of the object. Let’s say we have a List role for which Array is the Role-player. I want to be able, again. to reason that if I ask for the length of the List and find it greater than zero, that I can access at least one element of that List with confidence. Here, I don’t take it on faith, but can actually read the code of List to ensure it is so.

I would also be more sympathetic if this were a language like self where much more of the focus is on run-time and on objects. That was the route taken by Smalltalk inspectors. They don’t, at run-time, differentiate between what in our case would be an instance method and a Role method, because at run-time classes are in the background and Roles may not even exist (as is the case in trygve). But we invented Roles in DCI to bring some of the instance semantics forward to the source code. The goal was to able to reason about instance interactions statically. It also helped being able to separate the local reasoning of how Roles interact locally with their object from how Roles interact with each other.

There are strong analogies to features in existing programming languages when viewed from the perspective of the original intent of requires to let a Role encapsulate an object. Getters and setters, most popular in the Lisp world but also quite common the Smalltalk world, were often heralded as a way to access private data without violating encapsulation. That’s just horse shit: they provided a way to violate the encapsulation of the class within the constructs of the language. The friend construct in C++ is the same way: to be able to access the instance method of the Role-player while bypassing the Role interface is very much like the friend construct. As an example, Matthew argues:

On 16 May 2016, at 01:47, Matthew Browne <mbro...@gmail.com> wrote:

The other thing that bothers me a bit about prohibiting direct access to instance methods from other roles is that it maintains what I see as essentially a compile-time separation at run-time.

By this reasoning it should be possible to access private instance methods of the Role-player from within the Role. And, by the reasoning of the rest of his mail, since they are part of the Role-playing object, they should be available to the entire Context if they are declared in the requires section. So I need guidance here to make sense of this. Are you saying:

1. That public and private make sense only to document relationships between instance methods of classes?
2. That public and private should be eliminated?
3. That objects need some kind of tag to carry the public / private information into run time?

I am not being facetious. The iAPX432 processor by Intel was a machine based on Role-based addressing, The Roles were supported in hardware by pointers called capabilities. A capability governed the ability of the holder to refer to individual fields of an object. Different holders of different capabilities to the same object would or would not be able to send certain messages or access certain fields depending on the permissions the capability gave to them.

We implemented the dual of this model back in the early 1980s in a language called C++/P, for which I was the principle language designer, and for which Jim Vandendorpe designed the run-time software environment and Rich Delzenero led the hardware architecture and design. The language had protection domains, and afforded different holders of pointers to the same object different permissions to access the object. Domains worked hierarchically along scope boundaries. Are you saying we need a capability-based machine to implement DCI? Or that we throw out public and private altogether? Your arguments are otherwise arbitrary and, for now, I discount them.

The issue of run-time permissions has never before arisen in DCI and I think to raise it now would be only a rationalisation in hindsight.

Exposing these methods makes their invocation largely uncontextualized. Formally, yes, because trygve doesn’t allow these scripts to be invoked from outside the Context, they are technically contextualized. But one needs to search the entire Context to find all possible contexts of invocation (more on that later), and it becomes impossible (again, a carefully chosen word) to reason about their timing relative to each other. Within a Context the timing is within the reason of the use case. Contrary to what I envisioned, our Contexts are rarely small. It makes the code much harder to read, much harder to reason about, than if they were small. And I think it is a good sign that they are large. Alan Kay once bemoaned the fact that we had backed ourselves into a corner by making objects too small. That means we need software engineering features that support programming-in-the-large, and that means well-articulated interfaces.

If the Role needs access to an instance script of its Role-player, it shouldn’t force me to worry about how invocations of that instance script from anywhere in the Context might adversely affect the state of the object. But that’s what is being asked for here. If I add code to a Role method that causes it to need access to a new instance method, and I add that instance method to the requires declarations, I have instantly opened Pandora’s box to the point where I must reason about how invocation of that instance method, at any time, from any object, playing any Role, may violate assumptions about how the Role interacts with the Role-player.

The argument has been made:

On 16 May 2016, at 01:47, Matthew Browne <mbro...@gmail.com> wrote:

So while it may be true that it's harder to reason about the state of a role player when you have to look outside the role to do so, you certainly shouldn't have to look outside the Context to do so.

Yet the Contexts in Matt’s trivial examples (e.g., https://github.com/DCI/scaladci/blob/master/examples/src/test/scala/scaladci/examples/ShoppingCart7.scala) are over 80 lines long. This is much larger than the “chunk” recommended as a unit of reasoning for good program comprehension, and a bit much to search while looking for all the loci that may change the state of an object without the Role being in control. Our Contexts all tend to be big. Egon’s Physics context is about 170 lines long. It largely works because the state of each object can be understood in terms of the transitions within its Role. So I dismiss this argument as well.

Further to this point, consider this concrete example:

context Context {
role auditor {
public void audit() {
// some auditing code
}
}
role list {
public void add(String item) { atPut(incrementIndex(), item) }
public String getAt(int theIndex) {
String retval = at(translateIndex(theIndex));
return at(translateIndex(theIndex))
}
private int translateIndex(int i) { return i + 1 }
private int incrementIndex() {
String theIndex = at(0);
int rawIndex = 1;
if (theIndex is null) {
atPut(0, "1")
} else {
rawIndex = theIndex.toInteger() + 1;
atPut(0, rawIndex.toString())
}
return rawIndex
}
public int size() // publish
} requires {
int size();
String at(int theIndex);
void atPut(int theIndex, String object)
}
public Context() { list = new String [16] }
public void trigger() {
list.add("123");
list.add("456");
auditor.audit();
String j = list.getAt(1);
System.out.format("list element is %s\n", j)
}
}

new Context().trigger

Any reasonable analysis of this code would conclude that it should print:

list element is 456

And because list encapsulates its object, no code that any other person (certainly not us — we’re well-meaning, after all) could introduce in another Role could change a list from being able to uphold list-like properties. And we design list  reason about it, and review it as an encapsulated Role that maintains the correct state of its Role-player. Yet all we need to do is add one line of code in far-away auditor:

list.atPut(2, "789”) // instead of “some auditing code"

and voilà! list has stopped working, in spite of the fact that all of its code is correct, reasoned about, and for that matter, tested.

The upshot? We must treat Roles as loci of encapsulation. To leave elements of the requires contract accessible outside the Role is a simple violation of encapsulation of the Role. There is never any call for this. Any justification for doing so flies in the face of other more fundamental axioms we long ago seem to have agreed about.

I’m sure that when some of you see this you’ll start recommending further changes around the edge of the language to make atPut private and accessible only to list. Does the corresponding instance method also have to be private? Or, if it is already private, can we access it at all? Is there a difference between being private to the Role-player, private to the object-cum-list-Role, the other Roles in the Context, and the world at large? Such recommendations are epicycles — local fantasies made in the vacuum of lacking broader consideration not only of language design, but of repercussions for the computational model.

This mail responds to the many such suggestions I’ve received over the past week or two.

Conclusion: We need a place in DCI languages where the Role / object interface is made explicit, as the longstanding discussions on this mailing list have maintained. This articulation needs to be separate from the Role / Context interface, which is already explicitly manifest in the interface of Roles in trygve and very many other DCI implementations. There are both source-code level and run-time object identity considerations and balancing them is a complex issue entailing not only the computational model but language feature orthogonality as well. The Role is both a name for an object within a given enactment of a given Context, and the home of Role methods. Because a Role is a locus of coordinated enactment it must, for the sake of the consistency of its object, ideally be able to encapsulate that object’s transformation in response to any and all messages it has received within a Context: i.e., to be able to encapsulate its state. While no language in general can guarantee such encapsulation, warnings are in order when the prospect of violating these guarantees can be detected within the language framework. Straightforward mitigation of these warnings stands in line with longstanding programming language tradition.

In this mail I want to point out that language design is hard, particularly with respect to the concern for feature orthogonality. I wanted to point out that most of what people are asking for will have surprising consequences elsewhere in the language, even if it would reap any benefit within the scope of the discussions folks have raised — which, to me, is a dubious presumption in the first place. I have tried out probably thousands of alternative implementations (you can see at least hundreds of them in the GitHub deltas) exploring these issues in trygve, and many more in the earlier years of work on the C++ and Ruby versions over many years. I came to these endpoints the hard way with much thought for the repercussions.

I hope that future discussions will come equally well-armed, and that future Emails will either be caveated with "hey-let’s-explore-this-crazy-idea" or “I, too, have thought about this for several years, have experimented, and here is the running code that demonstrates my argument." Therefore, from this point forward, I am likely to pay a lot more attention to arguments that are backed by concrete proposals, in code, that runs. Concrete proposals can’t abstract away questions such as whether to allow object-thinking to reign to the point of allowing invocation of private methods. So with respect to trygve I will look for people to start exploring these issues on branches and sending pull requests. On one hand, talk is cheap. On the other hand, I had to expend several days writing this lengthy mail because I am often arguing against ghosts: there is often no concrete counter-example I can offer to the arguments alone, in code. The whole idea of creating trygve was to make arguments concrete.

Hai Quang Kim

unread,
May 18, 2016, 12:11:10 AM5/18/16
to object-composition
I just want to share my real world experience with this issue.

In my context, I want to define a Role 

RoleInterface MyRole

Then I define Role Interface as we use 'require' in trygve language.
This works well for new data object type, and for Roles that I plan to have different types of data object to play.
--> software reuse issue here

As we 'agreed' before software reuse is hard. 

But for many cases I just simply want a specific type of data object to play that Role.
Eg.

Panel WindowRole

In this case, I don't have to define the Role interface, I am allowing all the data methods of Panel to be accessible.
Maybe I am lazy and I don't plan to support any kind of data objects to play 'WindowRole' in the future

I have a problem to solve, and that help me to move on to make it work. May be in the phase to make it right I will think about the Role Interface again. (not an easy problem while working with legacy system unless I use wrappers)

I think the problem we have with 'require' here in trygve's examples is: 
If I want to use system's legacy objects like Panel, int, float...
I have no idea what to define in require.

Remember operator+ in 'int' for money transfer example.
It is easy to use, but it is hard to define in require section.
I think Cope is the only one knows where it is defined :)

This is just UX of the programming language.
We can be strict to define require every where or can be flexible to allow programmer to define the Role as a specific type to avoid wring that require. For each case we are trading of reuse-ability/readability of Role and the easy of getting thing working before getting it right.

So I just want to suggest new syntax:

role THE_PANEL : Panel
{
     role methods()
}
[no require, I can use all object methods]

In Context

THE_PANEL.role methods()
THE_PANEL.object methods()

Basically we have 2 doors, and user should have the options to state which one should be open/close from Context :)

/quang

Matthew Browne

unread,
May 18, 2016, 12:44:13 AM5/18/16
to object-co...@googlegroups.com

Cope,
That was a very thorough and illuminating post! My response won't be nearly as thorough, largely because I don't actually have a strong objection to the instance method access restriction in the current version of trygve. While obviously I was leaning against it, my main argument was not that it was necessarily bad - and it may even be very good as you are arguing - but that there were factors that deserved further consideration before baking this restriction into the language for good.

The restriction could still be lifted later of course, but IMO it's better to be quite sure about a restriction before putting it in in the first place. Such certainty requires considering various opinions, researching history and theory, and if possible some empirical evidence based on the experience of more than just one or two programmers (which is tricky since not many people are using DCI in the wild yet, so we're mostly left with extrapolating more or less analogous evidence from other paradigms). Especially given this latest post, I think you have done all of these, although obviously we don't have any real-world evidence about this specific to DCI since trygve is a brand new language. I don't claim to have a strong theoretical backing for the points I was raising, aside from my personal theories about DCI that I have formed during my time as a member of this group and as a DCI programmer (note that in general, my "personal theories" are still in many ways grounded in earlier theory, most of which I've learned through the DCI literature). The purpose of my previous post was to at least raise some questions since I think this deserves further discussion, so I am glad my points have been heard and I think there might be some value in clarifying and commenting further on some things...

As a DCI implementation author, I still hesitate to add a similar restriction to the JS implementation I am currently focused on, but you have certainly made a strong case for it and I will be watching any future discussions about this carefully as I am far from fully decided on this.

See more comments inline.


On 5/17/16 4:54 PM, James O Coplien wrote:
So it went into trygve from the beginning. I think it’s a good thing in its own right. We have often discussed it here on the list. We have talked about making it explicit. I don’t think anyone else has done that (except Matthew in his new language variant). I have.
A small comment - unless you are talking about the specific way in which trygve implements role-object contracts, there are indeed others who have implemented similar features. ScalaDCI and HaxeDCI have both had explicit role-object contracts for quite some time (most of the ScalaDCI examples use declared types but it supports duck-typed contracts as well). I think Rune said at one point that Marvin and/or Maroon have role-object contracts as well although I'm not sure I recall correctly (in any case they're not documented).

That is certainly a style of programming, but I wouldn’t call it DCI in terms of the pictures Trygve has drawn, the code he has written, and the discussions we have had here. The following code meets all the criteria people have been asking for but I hardly feel it rises to provide DCI’s benefits. It just makes identifiers into fat first-class administrative entities (talk about bureaucratic!)
I agree that the code example you posted was difficult to understand because it relied way too much on delegating the use case behavior to instance methods, making the Context abstract rather than compressed. But allowing an instance method to be accessed by other roles would still allow programmers to put behavior in the role methods and interact with the role via its role interface rather than its 'requires' interface as much as they deemed appropriate. So the rationale for the current restriction in trygve needs to be stronger than that. Automatically permitting access to instance methods from other roles is only problematic if the programmer calls instance methods directly from other roles in situations where they should not.
This code would compile and run if all the code on the other side of the trusted abstraction boundary were there in the classes. Because it is on the other side of the trusted abstraction boundary, that you are unable to read it shouldn’t make you nervous. It meets all the criteria posited by those advocating direct access of instance methods. Can you analyze the code to understand what it does? Good, if you can, because then we don’t need DCI and we can just go back to class-oriented programming. If you can’t, it just may be the fault of the reasoning that has been presented in recent mails. Point finale.
Consider the following example:

role Customer {
    public void changeUsername(String username);
}
requires {
    void changeUsername(String username);
}


How does promoting the instance method to a role method ensure the integrity of the role player any better than calling the changeUsername instance method directly?

It seems that for cases like the above, it doesn't matter much. If I understand correctly, the main value of the restriction comes when you want a role method to call an instance method that changes the state of the object, but there's no need for other roles to be able to call that instance method directly....and furthermore, allowing other roles to do so could potentially allow inappropriate changes to the state of the object:

role Customer {
    public void changeUsername(String username);
    public void changePasswordCarefully(String password) {
        //some use case-specific logic here
        changePassword(password);
    }
    //we do not expose the changePassword() instance method here
}
requires {
    void changeUsername(String username);
    void changePassword(String password);
}


(Obviously this is a contrived example, but hopefully it's sufficient to get the point across.)

So I do understand at least this aspect of why the restriction could be useful / important.

But things become less clear-cut when you consider that we're talking about public instance methods here (because if they weren't public, then the role wouldn't be able to call them in the first place).

Outside of any Context, the programmer can write code to mutate the state of the object via public instance methods in any sequence that they please and anywhere in the code base. That's a giant loophole. If the instance methods of an object should only be accessible by a certain set of roles, then that loophole needs to be closed.

Maybe the DCI paradigm calls for some new access levels or rules that better takes role-binding into account. On the other hand, maybe it should just be entirely up to the object's class to protect the object's integrity when its public methods are called. I'm not sure which approach is better. Hopefully we can get more concrete about that as we continue this discussion, and I agree that more concrete discussions are often far more productive.
(Another minor point - I'm glad that Marc acknowledged my contribution of that version of the shopping cart example, but I do want to credit Marc as the original creator of that example since my modifications to it probably fall short of calling it "my" example.)

Alright, that's enough for tonight - I might respond to a couple other points tomorrow.

Thanks,
Matt

Matthew Browne

unread,
May 18, 2016, 1:09:28 AM5/18/16
to object-co...@googlegroups.com

Hi Quang,
(When I write this it looks almost like I'm misspelling your name, but I'm just saying hi :) )

Thanks for your post. I just wanted to clarify that what you discussed here is actually a separate issue from what I was responding to. I already agree with Cope that role and context methods should only be allowed to access instance methods that are defined in the role-object contract. I'm still open to the idea of declared types for role-object contracts in some cases, but even in that case I'd still want the compiler to require a duck type as well so we can easily see at a glance all the instance methods that will be used in that Context, and so that methods not listed in the contract cannot be called.

The question I'm concerned about is more specific: once an instance method has been specified in a role-object contract, should it be automatically accessible so it can be called from anywhere in the context, or should only its own role have access to it by default? I think Trygve posted a concrete example earlier in this thread that triggers the warning we're discussing.

--

Rune Funch Søltoft

unread,
May 18, 2016, 2:07:09 AM5/18/16
to object-co...@googlegroups.com


Den 17. maj 2016 kl. 22.54 skrev James O Coplien <co...@gertrudandcope.com>:

So it went into trygve from the beginning. I think it’s a good thing in its own right. We have often discussed it here on the list. We have talked about making it explicit. I don’t think anyone else has done that (except Matthew in his new language variant). I have.
It has been part of Marvin from day one as well. I originally invented it to make the compiler implementation easier but found that it help communicate intend. I also tried out to other variations. One where the compiler relies on the DLR to do run time type checks and method lookups and one where you type the role. In general I prefer the role contract (requires) but found that using the typed version was a great help when integrating towards existing code

Trygve Reenskaug

unread,
May 18, 2016, 5:25:04 AM5/18/16
to object-co...@googlegroups.com

This is getting too advanced for me and and need some clarification.  I am talking  about the visibility of roleplayer methods as seen from other roles.

If I, in a role script in the frontloading context (PLAN role, frontload() script), write


     for (Activity act : model.allActivities()) act.setEarlyStart(void)

where setEarlyStart() is implemented as instance methods in all classes that can provide activity objects. I interpret you as saying that I don't know where I am with this code and might hit the halting problem. Even if I add
    requires {... void setEarlyStart(int start) ... }
the danger remains. But if I extend the ACTIVITY role with the declaration
    public void setEarlyStert(int start)
the I do know where I am. The halting problem has gone and everything is fine. My conclusion is that I have clearly misunderstood your arguments, so I would appreciate a clarification.

--
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 https://groups.google.com/group/object-composition.
For more options, visit https://groups.google.com/d/optout.

--

The essence of object orientation is that objects collaborate  to achieve a goal.
Trygve Reenskaug      
mailto: try...@ifi.uio.no
Morgedalsvn. 5A       
http://folk.uio.no/trygver/
N-0378 Oslo             
http://fullOO.info
Norway                     Tel: (+47) 22 49 57 27

James O Coplien

unread,
May 18, 2016, 6:54:53 AM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 06:11, Hai Quang Kim <wan...@gmail.com> wrote:

So I just want to suggest new syntax:

role THE_PANEL : Panel
{
     role methods()
}
[no require, I can use all object methods]

In Context

THE_PANEL.role methods()
THE_PANEL.object methods()

There is no way to arrange against exposing private methods to general Context code this way.

Is that what you want?

If so, private becomes meaningless.

It is also impossible to detect at compile time whether the Role-player will even have the given object methods, so you have opened the door for run-time uncertainty and “message not understood."

It sounds like you want Smalltalk (to be able to call object methods without declaring them, or potentially even if they don’t exist) together with abstract interfaces, à la Java, instead of DCI. But that’s fine. Smalltalk is the best Smalltalk there is.

This is what I meant about feature orthogonality and the need to think things through. Waiting for your pull request.

James O Coplien

unread,
May 18, 2016, 6:56:11 AM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:

I agree that the code example you posted was difficult to understand because it relied way too much on delegating the use case behavior to instance methods, making the Context abstract rather than compressed. 

That is exactly what was advocated, asked for, and stipulated, together with a “full stop.” I was made to believe that anything else would not be a properly formed Role.

James O Coplien

unread,
May 18, 2016, 7:07:52 AM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 08:07, Rune Funch Søltoft <funchs...@gmail.com> wrote:

It has been part of Marvin from day one as well. I originally invented it to make the compiler implementation easier but found that it help communicate intend. I also tried out to other variations. One where the compiler relies on the DLR to do run time type checks and method lookups and one where you type the role. In general I prefer the role contract (requires) but found that using the typed version was a great help when integrating towards existing code

Thanks for reminding me. It was long ago, and I think it’s less memorable to me because it was so similar to what I’ve done in trygve. Maybe Marvin even unconsciously influenced me.

James O Coplien

unread,
May 18, 2016, 7:08:37 AM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 11:24, Trygve Reenskaug <try...@ifi.uio.no> wrote:

 My conclusion is that I have clearly misunderstood your arguments, so I would appreciate a clarification.

Look at the last example in my long mail.

James O Coplien

unread,
May 18, 2016, 7:14:55 AM5/18/16
to object-co...@googlegroups.com
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:

But allowing an instance method to be accessed by other roles would still allow programmers to put behavior in the role methods and interact with the role via its role interface rather than its 'requires' interface as much as they deemed appropriate.

But now there is a reasonable locus (the Role) of understanding how the state may be changed. It is under the control of the writer of the Role methods. By exposing the requires interface in the Role interface you take away the ability of the Role author to do this.

With the trygve approach you can choose to expose the instance method interface in the Role interface, or to control access to the instance methods through the Role.

With your suggestion you are forced to expose the instance method interface as part of the Role interface, and cannot code the Role to regain control of access to the object.

Which one is more reminiscent of a bureaucracy? I give options. Yours forces what is commonly recognized as violation of encapsulation.


So the rationale for the current restriction in trygve needs to be stronger than that.

No, it doesn’t. What is weak about the above rationale? Seems pretty compelling to me — unless code comprehensibility is held to be irrelevant.

James O Coplien

unread,
May 18, 2016, 7:18:19 AM5/18/16
to object-co...@googlegroups.com
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:

How does promoting the instance method to a role method ensure the integrity of the role player any better than calling the changeUsername instance method directly?

It doesn’t. It just makes it visible, through a suitable, distinguished mechanism, that in your ego you have screamed “I KNOW BETTER THAN THE COMPILER!” and that you have taken conscious effort to declare that and to tell the compiler to go away. It’s just a way of shutting up the compiler, but keeping all readers on notice that the Role is a leaky abstraction.

Please go back and read my post carefully. I know it was long and that some points get lost when reading a long mail, but I feel that I made this clear.

Maybe I should have stuck with an uglier mitigation like #pragma to make it more visible that the programmer is insisting on doing something unseemly here.

James O Coplien

unread,
May 18, 2016, 7:20:42 AM5/18/16
to object-co...@googlegroups.com
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:

But things become less clear-cut when you consider that we're talking about public instance methods here (because if they weren't public, then the role wouldn't be able to call them in the first place).

What is your rationale for that claim? It goes against your argumentation for thinking about the object instead of thinking about the source constructs. That method, whether private or public, is nonetheless part of the object. What part of your argumentation brings the source properties to bear here, but keeps them from coming into the arguments for Role integrity? You seem to be arguing two separate ends of the same stick, and you can’t have it both ways.

James O Coplien

unread,
May 18, 2016, 7:23:52 AM5/18/16
to object-co...@googlegroups.com
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:

Outside of any Context, the programmer can write code to mutate the state of the object via public instance methods in any sequence that they please and anywhere in the code base. That's a giant loophole. If the instance methods of an object should only be accessible by a certain set of roles, then that loophole needs to be closed.

Maybe the DCI paradigm calls for some new access levels or rules that better takes role-binding into account. On the other hand, maybe it should just be entirely up to the object's class to protect the object's integrity when its public methods are called. I'm not sure which approach is better. Hopefully we can get more concrete about that as we continue this discussion, and I agree that more concrete discussions are often far more productive.

And that is why I brought up the iAPX432, which indeed provided facilities for doing that, and implemented them in an Ada compiler. And it was a facility provided by the C++/P language we did back in 1983 or so.

But it’s irrelevant. This is about code comprehensibility rather than formal proof of correctness. From the standpoint of formal correctness your argument could be used against any and all features that promote encapsulation. I think that both confuses the issue, and is not productive.

Trygve Reenskaug

unread,
May 18, 2016, 8:23:40 AM5/18/16
to object-co...@googlegroups.com
--
Which long mail? Date and time please.

Matthew Browne

unread,
May 18, 2016, 8:49:30 AM5/18/16
to object-co...@googlegroups.com
On 5/18/16 7:14 AM, James O Coplien wrote:
With the trygve approach you can choose to expose the instance method interface in the Role interface, or to control access to the instance methods through the Role.

With your suggestion you are forced to expose the instance method interface as part of the Role interface, and cannot code the Role to regain control of access to the object.

Which one is more reminiscent of a bureaucracy? I give options. Yours forces what is commonly recognized as violation of encapsulation.
This leads me to believe that the word "bureaucratic" is too ambiguous for this discussion. I agreed with Trygve that the restriction might be too "bureaucratic" because it imposes a rule on the programmer that could be unnecessary and a bit onerous if they know what they're doing, and which could also increase the learning curve for beginners. Beginners are likely to make mistakes no matter what, and a compiler warning in itself is not enough to teach them how to properly write roles if they're doing it wrong - but of course it might help, so I grant you have a point there. Your implementation indeed gives the programmer the ability to control access to instance methods more precisely, so it's less "bureaucratic" in the sense of freedom from default rules that might expose instance methods more than the programmer would like.

It's nice that the compiler gives a warning rather than an error, which of course weakens the argument that the compiler's current behavior is overly bureaucratic. I actually didn't realize it was only a warning until you pointed it out, because warnings look the same as errors (perhaps the code I was writing the last time I encountered this caused an actual compile error at the same time, or maybe I just didn't notice that the "Run" button had become active). It might be helpful to prefix all warning messages with "Warning: ".

So the rationale for the current restriction in trygve needs to be stronger than that.

No, it doesn’t. What is weak about the above rationale? Seems pretty compelling to me — unless code comprehensibility is held to be irrelevant.
My point was that the possibility that a programmer could write an overly abstract context and make the code hard or impossible to reason about (as in your code example) does not equate to the compiler encouraging this behavior or necessarily lead to the conclusion that the compiler must have a rule to prevent it. There are a lot of bad practices that no compiler can prevent, and the compiler shouldn't try to micromanage things to the point where the restrictions get in the way more than they help. But the restriction regarding role player access isn't too onerous (only a little sometimes) and could potentially be helpful, which is why I said I didn't have a strong objection to it. Perhaps I'll eventually come around to endorsing it, but I'm not there yet.

Matthew Browne

unread,
May 18, 2016, 9:11:48 AM5/18/16
to object-co...@googlegroups.com
On 5/18/16 7:18 AM, James O Coplien wrote:

On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:

How does promoting the instance method to a role method ensure the integrity of the role player any better than calling the changeUsername instance method directly?

It doesn’t. It just makes it visible, through a suitable, distinguished mechanism, that in your ego you have screamed “I KNOW BETTER THAN THE COMPILER!” and that you have taken conscious effort to declare that and to tell the compiler to go away. It’s just a way of shutting up the compiler, but keeping all readers on notice that the Role is a leaky abstraction.
This argument makes it sound like the code I posted is necessarily a bad practice:


    role Customer {
        public void changeUsername(String username);
    }
    requires {
        void changeUsername(String username);
    }


Whereas this is fine:

    role Customer {
        public void changeTheUsername(String username) {
            changeUsername(username);
        }
    }
    requires {
        void changeUsername(String username);
    }


I'm guessing that's not what you meant, since otherwise you probably wouldn't have added the feature that allows the first example. I just wanted to make clear that writing forwarding methods often does not make much of a practical difference. (I'm referring only to simple forwarding here, not cases where there should be a role method that does more than just call an instance method.) Forwarding methods should still be written if there's a reason to name the role script differently from the instance script, but most of the time we can reason about the role using the same method names as in the instance and it makes perfect sense.

Matthew Browne

unread,
May 18, 2016, 9:31:53 AM5/18/16
to object-co...@googlegroups.com

I would have no argument at all against more precise access restrictions for instance methods if instance methods in the requires {} section were public rather than private to the surrounding context by default. It's the assumption that they should generally be private that I disagree with. In any case I think the ability to make an instance method private to a given role would still be useful, e.g.:

    role Customer {
        ...
        private void changePassword(String password);
    }
    requires {
        void changePassword(String password);
    }

Or maybe:

    role Customer {
        ...
    }
    requires {
        local void changePassword(String password);
    }

(It would have to be an alternative word like "local" with this approach because "private" already has a meaning; if I understand correctly, "private" would mean that the role player's class should have a private method called changePassword...although I don't know why a Context programmer would care what private methods a role player had.)

Hai Quang Kim

unread,
May 18, 2016, 11:09:01 AM5/18/16
to object-composition
sorry I did not make my point clear enough.
2 things in my example:
1- I would like to define a type for Role so that I can skip the require part
eg.
Role WINDOW: Panel
{
     void myRoleMethod() {
         self.DataObjectMethods()   // public methods only, I am not sure about calling private methods
     }
     // no require section needed
}

2-From our side of Role:
Context/Other Role can trigger RoleMethod (or public method of DataObject --> unified interface)
But I am fine with not exposing data object methods to other Role too.
With the role method I have the power to express the message better.
Indeed many times I write wrapper role method to have better name for the communication.

I am not saying which direction is better since it is too advanced for me too :)
I am saying from user point of view because sometimes I am stuck with the require section while I just want specific type for a Role.

Of course my experience comes with working in existing system.
And trygve language is new and it is good time to try new idea.
so maybe it is not worth to care about legacy issue then.


/quang

James O Coplien

unread,
May 18, 2016, 11:38:35 AM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 14:49, Matthew Browne <mbro...@gmail.com> wrote:

This leads me to believe that the word "bureaucratic" is too ambiguous for this discussion.

I think we have more important things to talk about than what the right perjorative term is. Trygve coined it: not I.

Is there some message or action I was supposed to take from the rest of your mail?

James O Coplien

unread,
May 18, 2016, 12:03:04 PM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 15:31, Matthew Browne <mbro...@gmail.com> wrote:

I would have no argument at all against more precise access restrictions for instance methods if instance methods in the requires {} section were public rather than private to the surrounding context by default. It's the assumption that they should generally be private that I disagree with. In any case I think the ability to make an instance method private to a given role would still be useful, e.g.:

I fully understand your position. My analysis of code comprehensibility holds this argument in high suspicion, based largely on how languages have helped express encapsulation for decades, and based on the foundational principles of DCI that we be able to understand what’s going on by reading the code. Keeping these methods private to the Role makes possible the mental chunking (at the object level) necessary to code comprehension. Just one leak makes the code orders of magnitude more difficult to reason about.

James O Coplien

unread,
May 18, 2016, 12:08:11 PM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 17:09, Hai Quang Kim <wan...@gmail.com> wrote:

eg.
Role WINDOW: Panel
{
     void myRoleMethod() {
         self.DataObjectMethods()   // public methods only, I am not sure about calling private methods
     }
     // no require section needed
}

You can of course do that in C++ because you know — at compile time — what every class of every Role-player might be. You can reason about run time access in terms of public access control in the code at compile time.

That’s another discussion. C++ is the only language (in DCI land) I know where this is true. No argument with you there. The trygve language is exploring another set of design tradeoffs because I feel that, if you’re willing to predetermine the classes of all Role-players for a given Role, that C++ already does that job about as well as it can be done.

So you can still use C++. It’s the best C++ there is. And there’s much written about it in the “Lean Arch” book.

Now, I’ll leave you with that and go back to a more dynamic DCI.

Matthew Browne

unread,
May 18, 2016, 12:11:10 PM5/18/16
to object-co...@googlegroups.com
From that particular mail, the only potential action item is adding "Warning: " to the beginning of all warning messages. I can submit a pull request for that if you're open to it.

My more serious proposal is in the next mail I sent, which I see you just replied to. I would like to wait and hear what other members of the group have to say about it before pursuing that proposal further.
--
Sent from my Android device with K-9 Mail.

James O Coplien

unread,
May 18, 2016, 12:36:04 PM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 18:08, Matthew Browne <mbro...@gmail.com> wrote:

From that particular mail, the only potential action item is adding "Warning: " to the beginning of all warning messages. I can submit a pull request for that if you're open to it.

That’s a really great suggestion and I appreciate you for making it. Why didn’t I think of that? I’m glad you caught it.

I would be happy to take a pull request that solves this and which finally puts this important issue to rest.

I have done a preliminary evaluation to see how much effort it would be and it appears to be…

Zero.

I think every warning very clearly features the word “WARNING” in all caps as its first word. (There is one potential exception in the case where someone accidentally includes a gratuitous “private” access modifier where it is presumed anyhow.)

So I guess I *did* think of that.

Remember in an earlier mail I talked about chasing ghosts? Here I am again. I want us to be expending energy solving concrete problems that have been subjected to careful analysis. Here we were, ready to hit the keyboard and start coding up a solution to something that was never a problem in the first place. Many more such problems are more subtle and the arguments underscoring their superfluity are more difficult to make, Here, all I need to do is ask for a single example of a problem.

We could make an entire list of desiderata about what should be in a compiler, and then have a big review exercise to see if the trygve compiler contains each of them. I would rather see the discussions in this group revolve around issues that 1. are real and 2. whose solutions have lasting leverage that are game-changers in programming. Those are foundations for this group to do greater work. We have large enough challenges ahead of us that we have to invent sins that aren’t there. It’s a very American thing dating back to the Pilgrims’ need to invent new sins for want of sin in society. And there were many ceremonies of that day analogous to many programming and software engineering practices of today — like drowning witches.

Let’s get back to work. Real work. Not makework.


My more serious proposal is in the next mail I sent,

WARNING: I have not yet checked whether trygve already handles that one properly.

Matthew Browne

unread,
May 18, 2016, 1:14:02 PM5/18/16
to object-co...@googlegroups.com
On 5/18/16 12:35 PM, James O Coplien wrote:
> I think every warning very clearly features the word “WARNING” in all
> caps as its first word. (There is one potential exception in the case
> where someone accidentally includes a gratuitous “private” access
> modifier where it is presumed anyhow.)
When I try to access an instance method directly from another role, I
get the following message:
> NONCOMPLIANT: Trying to enact object script `foo()' without using the
> interface of the Role it is playing: `B'.
It does not say "WARNING" at the beginning.

I realize this is a relatively minor thing and perhaps you think it's
unimportant, but I offered to make the change and submit a pull request.
Is it so unimportant that you would be unwilling to accept the pull request?

Raoul Duke

unread,
May 18, 2016, 1:37:23 PM5/18/16
to object-co...@googlegroups.com
I appreciate Dr. Cope's push to core matters!!!

I also appreciate Matthew's attention to UX detail re: the warning message :-)


Rune Funch Søltoft

unread,
May 18, 2016, 2:19:57 PM5/18/16
to object-co...@googlegroups.com
Not a reply to any specific message. 

I'd say that at least a substantial part of the comprehendability problem would be gone with an immutable approach and you all know I think that's where we should gone. Which is also part of why I'm not very active debating trygve. I find, for valid reasons, that trygve is imposing a structure that creates quite a few of the problems we're discussing. Whether that's actual true I don't know because I haven't invested time in building an environment where I could experiment. 
All that said I think the requires section should be private. With no way of changing that to public.  If there should be an exception if should be to allow const methods to be exposed. If it weren't tempting if not faith then the halting problem I would also suggest to make wrapping a non const method simply to expose it as an error. One could implement a very naïve version that you could still circumvent but doing so would also mean that you really really wanted to break encapsulation and should know there'd be dragons and mountain dwarves 

Mvh
Rune

Den 18. maj 2016 kl. 19.37 skrev Raoul Duke <rao...@gmail.com>:

I appreciate Dr. Cope's push to core matters!!!

I also appreciate Matthew's attention to UX detail re: the warning message :-)


Raoul Duke

unread,
May 18, 2016, 2:24:02 PM5/18/16
to object-co...@googlegroups.com
I do wish somebody smart had time and energy to take a pure fp / applicative / whatever you feel is opposite mutable OO as possible approach to see how it would work out. :-)

James O Coplien

unread,
May 18, 2016, 3:03:19 PM5/18/16
to object-co...@googlegroups.com
That’s because NONCOMPLIANT is a different error category than WARNING. In saying “Warning” earlier I was informally implying “non-fatal.”

Do we need to design an error taxonomy? There are IEE and IEEE standards for such things. Perhaps it should say “NON-FATAL” instead of “WARNING”?

James O Coplien

unread,
May 18, 2016, 3:05:11 PM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 19:37, Raoul Duke <rao...@gmail.com> wrote:

 Dr. Cope's

I do NOT deliver babies.

James O Coplien

unread,
May 18, 2016, 3:18:17 PM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 20:19, Rune Funch Søltoft <funchs...@gmail.com> wrote:

I'd say that at least a substantial part of the comprehendability problem would be gone with an immutable approach and you all know I think that's where we should gone.

I agree, but I consider trygve to be a necessary interim step. People are still struggling with the basics, let alone having to fight another paradigm shift (and moving to dataflow computation / immutability is a paradigm shift, unlike, say, the move from Pascal to Smalltalk or C to C++, as Larry Constantine has often pointed out).

I see trygve as a training ground to give people the access to DCI fundamentals, and we in kind will benefit from watching their experience and hearing their feedback. This whole warning thing I see as an unfortunate detour into second-year CS undergraduate issues and it’s frustrating (it probably shows). It’s already caused a stir, to say the least, in the DDD world, which all of a sudden is holding its head over the issue of whither use cases and of multiple “tops” of complex systems.

The trygve language does have a few side-effect-free elements as a way of dipping our toes in that water, but it was hardly developed with that model in mind. A very knowledgeable trygve programmer could almost use it in a Lisp style. I think that will have to wait a while. But maybe not. If someone creates a “competing” side-effect-free (except for events) language that we can study in parallel, I’d be thrilled. One hidden agenda I have with trygve is to get DCI into adaptation land (and it’s working — there have been over 100 DCI Tweets on Twitter in the past 48 hours, that I know of) and that entails a “worse is better” approach (also working). I suspect that jumping to a functional language wouldn’t afford us this feedback — and publicity.

Lao Tse said: Timing is everything. I think that side-effect-free computation will have its day. Maybe its day can be “now” but it will require a full-time spokesperson to make it work. It would likely have to be someone reading this mail.

— Cope

Matthew Browne

unread,
May 18, 2016, 3:19:53 PM5/18/16
to object-co...@googlegroups.com

Rune,
In your vision, would all domain objects (or even all objects generally?) be immutable, or only those that are pure data? I remember you previously pointed out the distinction between state and data and you said you still thought there was a place for domain objects with mutable properties, but I'm not clear whether or not that was only in reference to non-FP languages.

James O Coplien

unread,
May 18, 2016, 3:20:30 PM5/18/16
to object-co...@googlegroups.com
On 18 May 2016, at 20:19, Rune Funch Søltoft <funchs...@gmail.com> wrote:

All that said I think the requires section should be private. With no way of changing that to public.

This is just so frigging obvious to me. What ever happened to encapsulation?

James O Coplien

unread,
May 18, 2016, 3:20:37 PM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 20:24, Raoul Duke <rao...@gmail.com> wrote:

I do wish somebody smart had time and energy to take a pure fp / applicative / whatever you feel is opposite mutable OO as possible approach to see how it would work out. :-)

In the Pattern community, the phrase “I wish somebody would…” is immediately interpreted into: “I volunteer."

Matthew Browne

unread,
May 18, 2016, 3:30:34 PM5/18/16
to object-co...@googlegroups.com
Encapsulation has historically been about encapsulation within the object boundary. Differentiating between an object boundary and a role boundary is a new concept introduced by DCI (well, and maybe some earlier role-based programming models too). So you may be right, but there are new and different considerations here. And I fail to see how the absence of any way of promoting an instance method to a role method would be a good thing...as we have seen, it frequently leads to awkwardly-named methods that do nothing more than forward to the instance methods. This statement is not meant to contradict anything I said previously (i.e. I recognize that calling instance methods directly can be overdone and lead to problems).

James O Coplien

unread,
May 18, 2016, 4:05:43 PM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 21:30, Matthew Browne <mbro...@gmail.com> wrote:

Encapsulation has historically been about encapsulation within the object boundary.

No, you have the view of a recent newcomer to computing — i.e., someone who learned computing after about 1986. Constantine had fully developed the concepts of coupling and cohesion long before objects, and Parnas had developed the notions of encapsulation even earlier. There was no “object boundary” in their world.

For example, FORTRAN procedures are a perfectly good encapsulation of algorithm. C language structs and Pascal records were all about encapsulation.

And encapsulation is just a technique — it is not the prize. Encapsulation is an information-hiding technique. If you hide all information, you don’t have a system. So good design encapsulates what Parnas called “design secrets” but publish the stuff necessary for the connectivity of the system.

All of the arguments about promiscuously exposing instance methods of Role-players hopelessly confuse these issues, but I know that in most cases I can’t build on peoples’ familiarity with these first principles to explain the design decisions I’ve made in the language. There are deeper things going on here about the mental models by which people group and reason about related concepts and state that pre-date DCI by several decades. In the end the foundations of the argument have nothing to do per se with "object boundaries."


And I fail to see how the absence of any way of promoting an instance method to a role method would be a good thing...as we have seen, it frequently leads to awkwardly-named methods that do nothing more than forward to the instance methods

I am totally lost about the antecedent to this argument, because trygve currently allows just such a promotion to avoid the awkward re-naming. Is this another ghost-non-problem? What is your point?


— Cope

Matthew Browne

unread,
May 18, 2016, 4:59:33 PM5/18/16
to object-co...@googlegroups.com
On 5/18/16 4:05 PM, James O Coplien wrote:
And I fail to see how the absence of any way of promoting an instance method to a role method would be a good thing...as we have seen, it frequently leads to awkwardly-named methods that do nothing more than forward to the instance methods

I am totally lost about the antecedent to this argument, because trygve currently allows just such a promotion to avoid the awkward re-naming. Is this another ghost-non-problem? What is your point?
I like how trygve allows this in the latest version, and was not arguing with that. I was responding to Rune's comment:


On 5/18/16 2:19 PM, Rune Funch Søltoft wrote:
All that said I think the requires section should be private. With no way of changing that to public.  If there should be an exception if should be to allow const methods to be exposed.
I think that forbidding all or all non-const instance methods from being promoted to role methods would be going too far.

James O Coplien

unread,
May 18, 2016, 5:04:40 PM5/18/16
to object-co...@googlegroups.com

On 18 May 2016, at 22:59, Matthew Browne <mbro...@gmail.com> wrote:

I think that forbidding all or all non-const instance methods from being promoted to role methods would be going too far.

Why?

We will move forward on insight rather than opinion…

I’d like to hear both sides of this — it sounds like an intriguing idea. The good news is that it’s concretely implementable, and I like things like that.

Matthew Browne

unread,
May 18, 2016, 5:13:50 PM5/18/16
to object-co...@googlegroups.com
On 5/18/16 5:04 PM, James O Coplien wrote:
On 18 May 2016, at 22:59, Matthew Browne <mbro...@gmail.com> wrote:

I think that forbidding all or all non-const instance methods from being promoted to role methods would be going too far.

Why?
Because of the awkwardly-named forwarding methods that would result and add no actual benefit to the code. For this reason, it would certainly not be advantageous to completely remove the instance method promotion feature. If it were allowed but only for const methods as Rune suggested, then that would reduce the naming problem perhaps by half (more or less depending on how many non-const methods there are). I might have been too hasty in dismissing the limitation of this feature to const methods only; maybe the naming/verbosity problem wouldn't be such a nuisance then, since it wouldn't happen as often.

Matthew Browne

unread,
May 18, 2016, 7:23:13 PM5/18/16
to object-co...@googlegroups.com
Assuming it should still say "NONCOMPLIANT", then changing it to either "Warning: NONCOMPLIANT" or "Non-Fatal: NONCOMPLIANT" would work. I am not familiar with the standards for error messages, but the goal is to make the difference between an error and a warning (here I'm using the more informal meaning of "warning") more obvious.

Hai Quang Kim

unread,
May 19, 2016, 2:28:05 AM5/19/16
to object-composition
It took me a long time to realize that association link in UML is very dangerous.
Every time I add a link in class diagram, that means I introduce communication channel for a ton of objects.

In this case, we must be careful when drawing a link not only from Role to Role but also from Role to Data Object too.

Every link really matters then :)

I always think trygve language is more on static side like C++ than dynamic side like ruby.
I didn't know that you want the Role to be more dynamic.

That why I feel it is hard to code in trygve. And I am not sure it is the editor or the language.
But now I think it is more about the mindset.

Many of us here like DCI's Role network.

But trygve's DCI has more than just the network.

It has a bit of formal method in side it. (like Z, I studied formal method Z before, but never ever really use them)

I think for that formal part, new generation of programmers might not care about it that much (scripting, dynamic language...)

But I totally agree with what you are trying to push for.

Btw, I brought this up because I am not the only one think it that way. (no offense here)

That why I bring this up, just in case we need more input for next move.


"The main reason for that is that the "Display" is composed of several interacting pieces, but the pieces that play the roles are very concrete, i.e. 

	private Panel panel_;
	private Frame frame_;
	private Mouse mouse_;

Ideally I would have made them into roles, but the "requires" section would have been to implement a specific type. Similarly for State and Breakout. (also for World)

The semantics of "you cannot call directly a role player method" and not being able to define a type as a requirement, made it difficult to make them into proper roles, so I opted for a "private member" instead.

	role Display requires Display;
	role World requires World;

Similarly I would have wanted to directly call the role-player methods e.g. instead of

	role Handler {
		public void collide(Entity A, Entity B){
			onCollision(A, B);
		}
	} requires {
		public void onCollision(Entity A, Entity B);
	}

write:

	role Handler requires CollisionHandler"

/quang

rune funch

unread,
May 19, 2016, 5:05:56 AM5/19/16
to object-co...@googlegroups.com

2016-05-18 21:30 GMT+02:00 Matthew Browne <mbro...@gmail.com>:
And I fail to see how the absence of any way of promoting an instance method to a role method would be a good thing...as we have seen, it frequently leads to awkwardly-named methods that do nothing more than forward to the instance methods.

I fail to see how you can deduce this. There's no cause and effect between not being able to break encapsulation and awkward naming. I see a much more likely cause and effect that is that the awkward naming comes from wanting to break the encapsulation in the first place. So instead of doing that try not to break the encapsulation and see if there's still awkward names. I most often see this when people are doing bottom up "design", That is start with the programmer mental model and gradually trying to transform that into the users mental model. That is "coincidentally" the reverse of DCIs goal of capturing the use cases in code. Try implementing the role. Not just specifying it but actually code it. Then realise what should be delegated to the roleplayer. I'd bet that this becomes a none issue or at least a very rare thing. 
Take it this way if the best name you can come up with for a role method is the same as the name of a required instance method then one of two are likely true

1) It belongs to the role
2) it belongs to the role player and not the role

and if neither is true it very likely a design smell. You have not abstracted. Your roleplayer is your role. The awkward naming is likely caused by trying to fix an issue in the form with a hammer of function. 

rune funch

unread,
May 19, 2016, 5:21:24 AM5/19/16
to object-co...@googlegroups.com
2016-05-18 21:19 GMT+02:00 Matthew Browne <mbro...@gmail.com>:

Rune,
In your vision, would all domain objects (or even all objects generally?) be immutable, or only those that are pure data? I remember you previously pointed out the distinction between state and data and you said you still thought there was a place for domain objects with mutable properties, but I'm not clear whether or not that was only in reference to non-FP languages.

when you refer to domain objects I'm not sure I know what you are referring to, My vision consist of two types of objects 
  1. Objects with no internal members (Ie intergers) or with not interaction between the internal members
  2. Objects with interaction between the internal members
I call the first data and the latter context. Ie that would be my definition of those terms in the DCI vocabulary.
The first are immutable. It's practice not common sense that has taught us that data is mutable. Common sense would dictate that the value of say 1 is always 1 it doesn't suddenly become 2 and if it does it's really confusing. This confusion comes from lack of a dichotomy between state and data. State can be represented by data but data is not state. The state of a system is by definition mutable.

A context is the structure of a system and the state of that system. I believe you can't change the structure of a system and still claim it's the same. You can change the state of the system and claim it's the same. E.g. if I'm playing call of duty my system is the same regardless of how many frames I've seen the past few seconds. However if I change the CPU I find that the structure has changed and I claim it's no longer the same system. So for a context I believe that the state can change but that the role binding (the embodiment of the structure) is immutable

James O Coplien

unread,
May 19, 2016, 6:15:43 AM5/19/16
to object-co...@googlegroups.com
On 19 May 2016, at 08:28, Hai Quang Kim <wan...@gmail.com> wrote:

In this case, we must be careful when drawing a link not only from Role to Role but also from Role to Data Object too.

The code structure should reflect the real-world relationships with respect to the partitioning we have chosen. We should be careful both to not omit any (as DDD does by bounding its contexts) or to introduce new ones that add artificial complexity. But we should be even more careful in choosing the partitioning criteria in a way that optimises cohesion (NOT of objects, but of whatever our partitioning knife gives us) and minimises accidental coupling.

A Role is a stateless collection of activities. To bypass any activity for the sole sake of accessing its state (through instance methods of the Role-player, which should do little more than oversee data access) is to set up the program to violate the semantics of the object with respect to its Role in the use case. It changes information into data.


Btw, I brought this up because I am not the only one think it that way. (no offense here)

Straddling the boundary between run-time and compile-time is hard for people who are used to thinking at compile time. For example, Matthew said yesterday that encapsulation “historically” has been about object boundaries. I wound back the clock to set the record straight. But even in the era of objects, this isn’t right. When we were working out the access control models for C++ around private and public, we became concerned about the independence of classes and invented protected. In most traditions the class, not the object, was the unit of encapsulation. This became institutionalized in C++ and its descendants (I would guess that Java and C# people think the same) and certainly in Eiffel, which is very class-centric.

I see many of these suggestions coming from a posture that confuses the two views (run-time and source) or which is missing a component of the overall model (the role of the Role in maintaining the integrity of its object). This perspective could well be fueled by people who have never worked on a large project, where contracts between the parts are crucial. Directly accessing Role-player’s instance methods, by using the Role as a means of access, makes them promiscuous and violates the encapsulation of the Role.


That why I bring this up, just in case we need more input for next move.

It looks like the main upshot of this plaint was that the poster wants to be able to substitute an interface name for the duck-typed list of signatures. I am considering that, but am very careful about mixing Role and class worlds in any way. Interfaces are a way of organising classes; Roles, a way of organising objects. The current duck typing makes the object perspective clear; using interfaces may cause people to revert to class thinking.

Egon Elbre

unread,
May 19, 2016, 6:31:08 AM5/19/16
to object-composition
Not exclusively.

e.g. Typescript interfaces work with objects/functions (https://www.typescriptlang.org/docs/handbook/interfaces.html).

Matthew Browne

unread,
May 19, 2016, 8:44:13 AM5/19/16
to object-co...@googlegroups.com
On 5/19/16 6:15 AM, James O Coplien wrote:
It looks like the main upshot of this plaint was that the poster wants to be able to substitute an interface name for the duck-typed list of signatures. I am considering that, but am very careful about mixing Role and class worlds in any way. Interfaces are a way of organising classes; Roles, a way of organising objects. The current duck typing makes the object perspective clear; using interfaces may cause people to revert to class thinking.
There's an important consideration for readability here that I think some people are overlooking: if you're the author of both the classes and the contexts, you may be quite familiar with the public methods of each class, but others reading your Context code in the future may not be (or even you yourself coming back to the code after a long time).

A lot of the benefits of OO come from the fact that data and behavior are combined in the same construct. In the past, some of these benefits were lost due to class-oriented programming (which is a good match for data structure but not necessarily behavior structure). With DCI, we can enjoy the benefits of both object-oriented and role-oriented programming. In order to fully support the object-oriented aspect and the ability to reason about the code as best we can, the programmer should be able to clearly see how data and behavior interact without having to look up the interfaces of all the potential role players.

So I like how the requires {} section in trygve makes you write out each instance method you'll be using. It makes the code more readable, and it also helps prevent narrowing the field of potential role players more than necessary by only specifying the methods needed for a particular role. That keeps roles generic and frees future (programmer) users of that Context from class and interface boundaries that are irrelevant to the context. And if you call an instance method that's not in the contract, then you get a compile error, which also helps to avoid the Context from making unnecessary assumptions about the class of the role player.

There may still be situations where it would be fine to use an interface name as a contract instead of a duck-typed list of method signatures - for example, common core libraries for which most programmers have the public interfaces memorized already. So I'm not opposed to implementations that allow role-object contracts specified using only interface or class names as long as they also support duck types. But I don't think that's really needed in trygve and I think the current duck-typing requirement encourages good practice. There could perhaps be a feature for declaring a reusable duck type within a Context, if several of the roles will be played by the same kind of role player, but that's a special case.

One area that I think still needs more exploration is using interface names in the requires {} section in addition to duck types. This could be useful for contracts whose duck type lists only one or two methods with common names (e.g. id() or size()). In that case, specifying an external interface could help prevent inappropriate objects from being allowed to play the role. On the other hand, you can already specify a parameter type in the constructor or wherever the role player is bound. (As to role players instantiated within the Context, the Context programmer should know better than to bind an inappropriate object to one of its roles.)

There's at least one other case where specifying an external interface might be helpful: iterable objects. In an earlier post I proposed that something like this could be useful:

https://groups.google.com/forum/#!topic/object-composition/xAND-sUF7dE

requires Iterable<{
    earlyStartTime(): int
}>



Matthew Browne

unread,
May 19, 2016, 9:33:03 AM5/19/16
to object-co...@googlegroups.com
On 5/19/16 5:05 AM, rune funch wrote:
Take it this way if the best name you can come up with for a role method is the same as the name of a required instance method then one of two are likely true

1) It belongs to the role
2) it belongs to the role player and not the role
Prior to the trygve language, my understanding of a role in DCI was that you could use the role identifier (call it R) to call either a role method or an instance method of the role player, from anywhere in the Context. trygve introduces the new concept (or at least new to me) that other roles can only access R's role interface and not the instance methods of R's role player, not even the methods in the requires section (which, to reiterate, is all I'm advocating for). So yes, I'm talking about methods that belong to the role player and not the role (your #2 above), but I don't agree that it's necessarily a problem to talk to a role-playing object using its instance interface. I think it should be possible to call either role methods or instance methods from anywhere in the Context; after all, they both belong to the same object, and using the identifier R is the way to address the object (endowed with whatever role methods R provides). And the language could still provide a way to mark certain instance methods as private to the role for those programmers who wanted more control.

All this talk about encapsulation has been written in a way that implies no new idea is being introduced here, and that trygve is just implementing already-established DCI fundamentals. It may indeed be building on such fundamentals, but it also introduces a new kind of encapsulation that did not exist in earlier implementations like Squeak and which even Trygve (who is a long-time proponent of encapsulation) objected to. I, too, recognize the importance of encapsulation but I'm not sure we need to go this far. In trygve, a role can access instance methods of its own role player, but other roles (in the same Context) cannot - that's a new restriction, and as such it needs to be discussed on its own merits (as we've been doing) and can't be dismissed simply by saying that it violates encapsulation. When did we decide that a role player's instance interface must be encapsulated and private from the perspective of other roles in the same context?

Consider the following example:

context GreetCustomer {
    public GreetCustomer(User customer) {
        Greeter = this;
        Customer = customer;
    }
   
    public void greet() {
        Greeter.greet();
    }
   
    stageprop Greeter {
        public void greet() {
            System.out.println("Welcome, " + Customer.firstName() + "!");
        }
    }
   
    stageprop Customer {} requires {
        public String firstName() const
    }
}

class User {
    public String firstName() const {return firstName_}
    private String firstName_;
    ...
}


This is a very trivial example and of course we wouldn't even need a Context for something like this, let alone two different roles. But I think it shows how it's quite natural and readable to call another role player's instance method directly. If you're limited to using only Customer's role interface and instance methods can't be promoted to role methods (which seems to be what you're advocating, although this is perhaps an example of where you'd make an exception since you said it might be OK for const methods), then I guess you'd have to do something like this:

    stageprop Greeter {
        public void greet() {
            System.out.println("Welcome, " + Customer.getFirstName() + "!");
        }
    }
  
    stageprop Customer {
        public String getFirstName() {return firstName()}
    }
    requires {
        public String firstName() const
    }

I don't see what value or benefit that brings to the code; it's just noise. Fortunately, the trygve language now allows the promotion of instance methods to role methods for such cases. I think that promotion should be the default, but I'm OK with it not being the default (as Cope said, some variance of perspective can be tolerated).

Matthew Browne

unread,
May 19, 2016, 9:39:07 AM5/19/16
to object-co...@googlegroups.com
On 5/18/16 3:18 PM, James O Coplien wrote:
One hidden agenda I have with trygve is to get DCI into adaptation land (and it’s working — there have been over 100 DCI Tweets on Twitter in the past 48 hours, that I know of) and that entails a “worse is better” approach (also working).
How many of these people do you suppose are actually trying DCI? I hope they are making their way to fulloo.info so they can see all the great resources and implementations we have already...

James O. Coplien

unread,
May 19, 2016, 9:52:42 AM5/19/16
to object-co...@googlegroups.com

On 18 May 2016, at 14:23, Trygve Reenskaug <try...@ifi.uio.no> wrote:

--
Which long mail? Date and time please.


So here is the example again. It shows a list of Strings that can be implemented (data part) in terms of any collection that obeys usual collection protocols such as at and atPut (and we’re not going to go into the discussion of why there isn’t a Collection interface — that’s a separate rathole). The list uses the first element of its implementation collection to store a String representation of the next available index in the list; the remaining elements at each index i respectively are bound to the (i - 1)st element of the list. One anticipates that the Role-player could be any collection. The code as it stands is set up to use a vector of Strings.

context Context {
role auditor {
public void audit() {
//list.atPut(2, "789")
}
}
role list {
public void add(String item) { atPut(incrementIndex(), item) }
public String getAt(int theIndex) {
String retval = at(translateIndex(theIndex));
return at(translateIndex(theIndex))
}
private int translateIndex(int i) { return i + 1 }
private int incrementIndex() {
String theIndex = if (size() > 0) at(0) else null;
int rawIndex = 1;
if (theIndex is null) {
atPut(0, "1")
} else {
rawIndex = theIndex.toInteger() + 1;
atPut(0, rawIndex.toString())
}
return rawIndex
}
public int size()
} requires {
int size() const;
String at(int theIndex) const;
void atPut(int theIndex, String object)
}
role Test {
public void access() {
String k = list.at(0);
System.out.println(k)
}
}
public Context() { list = new String [16] }
public void trigger() {
Test.access();
list.add("123");
list.add("456");
auditor.audit();
String j = list.getAt(1);
System.out.format("list element is %s\n", j)
}
}

The Role list comprises a set of scripts that maintain the integrity of the state of the Role-player with respect to its place in the use case. It maintains this integrity as long as we don’t violate its integrity.

Envision this Role as part of a 200-line Context, inside of which, somewhere, is buried the expression:

list.atPut(2, "789")

This violates the encapsulation of list because list doesn’t publish atPut  in its interface. And with good reason: mucking with the internal structure of the list violates its assumptions about how it maintains data.

The reason this breaks is because DCI classes are “barely smart data.” They are not information — certainly not in the context of the use case slices embedded in Role scripts. Another way of saying that is that they are context-free. Here, the first element of the internal data structure is being accessed without the contextualization given to it by the Role. This is a level of contextualization we don’t find in any of Trygve’s diagrams — but it’s there, and it’s important. Without realizing the need for this contextualization one of course can casually make Role-player instance methods promiscuous: even though they are data, the fact that they are accessed through a Role identifier gives the illusion that there is information. May be so, but the information has nothing to do with the contextualizaton of the Role.

There’s even a more sinister problem with this code. Delete the nasty line in Context.auditor.audit.  We still have the noncompliance message at line 33 because we’re directly accessing the at script of the list Role-player. Now we decide to use a Map instead of an array as the Role player. After all, it obeys the contract of the requires section. So change the initialization in the Context constructor;

public Context() { list = new Map<int, String>() }

Compile it. No problem. Now run it.

Oops. I’ll leave it to you to figure out what happened. Instead of trusting list to yield the size properly as an integer, we wanted directly to elicit the size as a String. Being clever and knowing (for absolute sure!) the implementation we introduced an optimization that works around the safe API provided by list. It worked until we changed the Role-player — even though the Role-player is also conformant to the requires clause.

Note that this even meets Rune’s criterion of only thus accessing const scripts. That’s not enough to save the day.

Someone will probably claim that the  requires clause should be more complete, replete with pre- and post-conditions. Am I allowed to use the word “bureaucratic” here? We need to rely on the design focus of the implementer of a Role. They follow the tradition of size suitable to designable “chunks.” A Context can build on these chunks to capture a socialized system use case. But if we start breaking the encapsulation of Roles, we violate the focus that chunking facilitates. This is why we have encapsulation at all (Parnas’ issue of “design secrets”).

Building reusable assets like list requires an immense investment in design and one does not cheat them lightly. (Actually, just to make something usable is equally hard — all design should be treated so seriously.) The promiscuous access non-compliance loophole makes it possible for you to pretend that you know more than the compiler does — and that you know more than the list designer knew about safe access to the object. What was a good abstraction boundary for the list designer is not necessarily a safe abstraction boundary for anyone focused on another Role, like Test, and certainly not for any Context script.

Exposing instance methods of Role-players, outside the Role, is dangerous. It decontextualizes information and reduces it to data.

Matthew Browne

unread,
May 19, 2016, 9:58:24 AM5/19/16
to object-co...@googlegroups.com
On 5/18/16 7:23 AM, James O Coplien wrote:
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:

Outside of any Context, the programmer can write code to mutate the state of the object via public instance methods in any sequence that they please and anywhere in the code base. That's a giant loophole. If the instance methods of an object should only be accessible by a certain set of roles, then that loophole needs to be closed.

Maybe the DCI paradigm calls for some new access levels or rules that better takes role-binding into account. On the other hand, maybe it should just be entirely up to the object's class to protect the object's integrity when its public methods are called. I'm not sure which approach is better. Hopefully we can get more concrete about that as we continue this discussion, and I agree that more concrete discussions are often far more productive.

And that is why I brought up the iAPX432, which indeed provided facilities for doing that, and implemented them in an Ada compiler. And it was a facility provided by the C++/P language we did back in 1983 or so.

But it’s irrelevant. This is about code comprehensibility rather than formal proof of correctness. From the standpoint of formal correctness your argument could be used against any and all features that promote encapsulation. I think that both confuses the issue, and is not productive.
Consider:

context UpdateAccountInformation {
    public UpdateAccountInformation(User customer) {
        Customer = customer;
    }
   
    public void updateCredentials(String username, String password) {...}
    public void updateUsername(String username) {...}
    public void updatePassword(String password) {
        Customer.changePasswordCarefully(password);
    }
    ...
   
    role Customer {
        public void changeUsername(String username);
        public void changePasswordCarefully(String password) {
            //some use case-specific logic here
            changePassword(password);
        }
        //we do not expose the changePassword() instance method here
    }
    requires {
        void changeUsername(String username);
        void changePassword(String password);
    }
}

class User {
    public void changeUsername(String username) {...}
    public void changePassword(String password) {...}
    ...
}

{
    User user = somePersistenceLibrary.getById(1);
    new UpdateAccountInformation(user).updatePassword('123');
   
    //We can still call changePassword() directly as long as we're talking to
    //an instance and not a role player:
    user.changePassword('456');
}


How is the ability to call changePassword() directly from outside the Context (or without addressing it via a role) not a loophole in your claimed encapsulation?

Rune Funch Søltoft

unread,
May 19, 2016, 10:02:18 AM5/19/16
to object-co...@googlegroups.com


So yes, I'm talking about methods that belong to the role player and not the role (your #2 above), No not my number two they are both exclusive. You are saying its both part of the role player and the role and I haven't yet seen a design where that's the case. I've seen (and possibly created) many implementations where it the case but that doesn't change that I believe it's a design smell and that its limits the context. 


Consider the following example:

context GreetCustomer {
    public GreetCustomer(User customer) {
        Greeter = this;
        Customer = customer;
    }
   
    public void greet() {
        Greeter.greet();
    }
   
    stageprop Greeter {
        public void greet() {
            System.out.println("Welcome, " + Customer.firstName() + "!");
        }
    }
   
    stageprop Customer {} requires {
        public String firstName() const
    }
}

class User {
    public String firstName() const {return firstName_}
    private String firstName_;
    ...
}


This is a very trivial example and of course we wouldn't even need a Context for something like this, let alone two different roles.
Since we're talking about proper design an example that you, yourself think isn't is hardly any good for the discussion. I'm arguing that it's likely a design smell and am basically doing is on the basis of the law of Demeter. That's encapsulation. 

But I think it shows how it's quite natural and readable to call another role player's instance method directly.
I've never claimed it wasn't. I've claimed I general find the wish to do so is based in lack of respect for the end users mental model or at least at bottom up design approach
If you're limited to using only Customer's role interface and instance methods can't be promoted to role methods (which seems to be what you're advocating, although this is perhaps an example of where you'd make an exception since you said it might be OK for const methods), then I guess you'd have to do something like this:

    stageprop Greeter {
        public void greet() {
            System.out.println("Welcome, " + Customer.getFirstName() + "!");
        }
    }
  
    stageprop Customer {
        public String getFirstName() {return firstName()}
    }
    requires {
        public String firstName() const
    }

In the above example your customer is in essence played by a string but your design of the role requires much more. In this particular case It might be hard to argue that there's any interaction between to persons. It's more one person using an attribute of another but the design is that of two persons interacting. This being a trivial example there might be more to the use case but with no use case it will be very hard for any of us to argue about what's proper design for that use case of a class of similar use cases

James O Coplien

unread,
May 19, 2016, 10:39:17 AM5/19/16
to object-co...@googlegroups.com

On 19 May 2016, at 15:58, Matthew Browne <mbro...@gmail.com> wrote:

How is the ability to call changePassword() directly from outside the Context (or without addressing it via a role) not a loophole in your claimed encapsulation?

Matthew,

You have posed much the same question before. At the time I explained in great detail why the question is irrelevant. Either you didn’t read it or you didn’t understand the answer.

I don’t know what I could say now that would either cause you to learn (learning is a change in behaviour) or even to read it. I think until there is a sign of learning, I will cease these interactions along this line of discourse.

Hint: DCI is about people, reasoning, and understanding, rather than computational completeness.

In the mean time, I’ll just point out that just about any imperative statement on a machine supporting a Von Neumann computational model comprises such a potential “loophole.” The proof is in a 1990s paper by Kaiser and Perry. To solve your problem is equivalent to guaranteeing that the language keeps any method invocation — including Role method invocation — for having unwanted side affects.

The kinds of computational models that Rune and I are looking at don’t have this problem. But we are solving a different problem (computational) than DCI solves (human comprehension of code.)

I await your pull request demonstrating your solution to the computational problem.

Matthew Browne

unread,
May 19, 2016, 11:09:43 AM5/19/16
to object-co...@googlegroups.com
On 5/19/16 10:39 AM, James O Coplien wrote:
On 19 May 2016, at 15:58, Matthew Browne <mbro...@gmail.com> wrote:

How is the ability to call changePassword() directly from outside the Context (or without addressing it via a role) not a loophole in your claimed encapsulation?

Matthew,

You have posed much the same question before. At the time I explained in great detail why the question is irrelevant.
Ok, thank you. I assume you are referring to your original long mail as well as your more recent reply which I pasted below. I have read all of your posts but I can certainly re-read your long one where I may have missed some things. I am fine with moving on to other topics but it does seem that this discussion has brought up a number of issues that are of interest not only to you and me but also the group as a whole.

James O. Coplien

unread,
May 19, 2016, 4:19:00 PM5/19/16
to object-co...@googlegroups.com

On 18 May 2016, at 19:37, Raoul Duke <rao...@gmail.com> wrote:

I also appreciate Matthew's attention to UX detail re: the warning message :-)

Well… kind of. It’s probably more like his primordial spirit crying out in desperation.

In good UX design, signs of importance are made visible in the user’s locus of attention. This is a psychological abstraction that usually corresponds to a visual field on the page called the focus.

The nearest statement I can find of the problem we’re solving, is this:

On 18 May 2016, at 14:49, Matthew Browne <mbro...@gmail.com> wrote:

I actually didn't realize it was only a warning until you pointed it out, because warnings look the same as errors (perhaps the code I was writing the last time I encountered this caused an actual compile error at the same time, or maybe I just didn't notice that the "Run" button had become active).


To summarize, the user wants to know if the program compiled. The environment already conveys this information by greying out the Run button if there are any errors. It was designed that way because, normally, the Run button will be the user’s locus of attention following a parse.

The longstanding compiler tradition for this problem is to print an error summary line when done. This presumes that the locus of attention is on the error log, which it might be. It does not serve the problem well to add the word “WARNING” at the front of the message because that isn’t the user locus of attention at that time.

There is the additional problem that there is no “Run” button in batch mode.

As for the message, I presumed people would interpret “NONCOMPLIANT” appropriately. That it is added as a modifier to the message itself suggests that it something different than a normal error, and broad social context should suggest that the compiler will allow it though it has wagged its finger at you. I guess I could print all of that as part of the error message but then I’m sure I’d be hearing a lot of mail about how error messages have to be shorter than X characters. In any case, no one (not once) on this list has asked what NONCOMPLIANT means, so I’m presuming you understand, and am puzzled both that there is puzzlement and that, in light of the puzzlement, no one has asked what NONCOMPLIANT means.

So I propose to add a message to the log that prints the number of errors. But it’s more than that, because this message both signals the number of errors and the termination of compilation. So I will also change the batch output protocol to output the same error message and to remove the current compilation-complete message.

Raoul Duke

unread,
May 19, 2016, 4:26:32 PM5/19/16
to object-co...@googlegroups.com
UX isn't just one thing telling you. It is a network of experiences working in concert.

If the class of things-the-compiler-can-say-about-what-didn't-go-100%-right can be split into warnings & errors and nothing else, then where does NONCOMPLIANT go of those 2, and whichever it is (apparently warning from what I glean from this thread so far) should be how it is formatted -- that seems reasonable to me. If it is warnings & errors & something else, and NC goes into 'something else' then it could say <SOMETHINGELSE> NONCOMPLIANT blah blah blah.

Yes it is bikeshedding at some level. But personally I think most systems end up with a death of a thousand cuts because they think it is all only bikeshedding.


Matthew Browne

unread,
May 19, 2016, 4:38:42 PM5/19/16
to object-co...@googlegroups.com

I'm interested to hear Cope's response to this. But I also just wanted to say that personally I'm fine with Cope's proposed solution of printing the number of error messages at the bottom. If at the bottom it had said:

1 warning
0 errors

...then I doubt I would have failed to notice that it was just a warning and not an error.

Thanks Raoul and Cope,
Matt

James O Coplien

unread,
May 19, 2016, 5:21:20 PM5/19/16
to object-co...@googlegroups.com

On 19 May 2016, at 22:38, Matthew Browne <mbro...@gmail.com> wrote:

I'm interested to hear Cope's response to this. But I also just wanted to say that personally I'm fine with Cope's proposed solution of printing the number of error messages at the bottom. If at the bottom it had said:

1 warning
0 errors

...then I doubt I would have failed to notice that it was just a warning and not an error.

Thanks Raoul and Cope,

Then it’s settled.

BTW — it also requires some rework on the automated internal tests, because the current logic parses the test output for comparing to the Gold — and it will require a re-golding of all tests. Test re-golding is not something to be done lightly so I’ll have to put this off until I have a few days in a row to spend on it. I’m not sure that will happen until next month. But it is on top of the queue, and I’m sure it will be worth it.
It is loading more messages.
0 new messages