Sharing behavior between contexts

123 views
Skip to first unread message

Matthew Browne

unread,
Apr 10, 2016, 1:12:19 PM4/10/16
to object-co...@googlegroups.com
Hi all,
I am wondering how best to approach reuse of similar, generic behavior among multiple Contexts. Consider the handling of events: a wide variety of different Context instances (and class instances too) may need to be able to register event listeners and notify them of events (depending on the requirements of the app of course).

I'm not considering inheritance for this because first of all, my interest at the moment is contexts, not classes; and even with classes, I don't think inheritance would be a good fit.

One option would be to use forwarding, and client code would access the EventEmitter (my name for the event-managing objec) directly:


context MyContext {
    private EventEmitter eventEmitter_;
   
    public MyContext() {
        eventEmitter_ = new EventEmitter();
    }
   
    public void init() {
        eventEmitter_.emit("initialized", new MyContextInitializedEvent());
    }
   
    public EventEmitter event() {
        return eventEmitter_;
    }
   
    //trigger method, roles
    //...
}

{
    MyContext ctx = new MyContext();
    ctx.event().on("initialized", new MyContextEventListener());
    ctx.init();
}


(Full code listing at the bottom of this email.)

One could argue that that breaks encapsulation, but I don't think that's necessarily true if all the EventEmitter behavior makes sense as part of the public interface of MyContext (which I think it does).

Another option is to use delegation, but it would get repetitive to do that with every event-emitting Context and I don't see the benefit over forwarding:

context MyContext {
    private EventEmitter eventEmitter_;
   
    public MyContext() {
        eventEmitter_ = new EventEmitter();
    }
   
    public void init() {
        eventEmitter_.emit("initialized", new MyContextInitializedEvent());
    }
   
    public void on(String eventName, EventListener listener) {
        eventEmitter_.on(eventName, listener);
    }
   
    public void emit(String eventName, ApplicationEvent e) const {
        eventEmitter_.emit(eventName, e);
    }
   
    //trigger method, roles
    //...
}

{
    MyContext ctx = new MyContext();
    ctx.on("initialized", new MyContextEventListener());
    ctx.init();
}



I would usually use a nested context for behavior shared by multiple contexts and have the nested context play a role, but that didn't seem to be a good fit here. (For one thing, while I envision various roles calling emit() on the EventEmitter, I don't envision the EventEmitter needing to interact with other roles, so I don't think it qualifies as a role.)



Full code listing (using the first version):

class ApplicationEvent {
    public ApplicationEvent() {
        timeOccurred_ = new Date();
    }
    public ApplicationEvent(Date timeOccurred) {
        timeOccurred_ = timeOccurred;
    }
    public String eventName() const {
        return "applicationEvent";
    }
    public Date timeOccurred() const {
        return timeOccurred_;
    }
    private Date timeOccurred_;
}

interface EventListener {
    public void trigger(ApplicationEvent e);
}

class EventEmitter {
    private Map<String, List<EventListener>> listenerMap_;
   
    public EventEmitter() {
        listenerMap_ = new Map<String, List<EventListener>>();
    }
   
    public void on(String eventName, EventListener listener) {
        List<EventListener> listeners = listenerMap_.get(eventName);
        if (listeners == null) {
            listeners = new List<EventListener>();
            listenerMap_.put(eventName, listeners);
        }
        listeners.add(listener);
    }
   
    public void emit(String eventName, ApplicationEvent e) const {
        List<EventListener> listeners = listenerMap_.get(eventName);
        if (listeners == null) return;
        for (EventListener listener: listeners) {
            listener.trigger(e);
        }
    }
}

class MyContextInitializedEvent extends ApplicationEvent {
    public String eventName() const {
        return "initialized";
    }
}

class MyContextEventListener implements EventListener {
    public void trigger(ApplicationEvent e) {
        System.out.println(e.eventName() + " event occurred");
    }
}

context MyContext {
    private EventEmitter eventEmitter_;
   
    public MyContext() {
        eventEmitter_ = new EventEmitter();
    }
   
    public void init() {
        eventEmitter_.emit("initialized", new MyContextInitializedEvent());
    }
   
    public EventEmitter event() {
        return eventEmitter_;
    }
   
    //trigger method, roles
    //...
}

{
    MyContext ctx = new MyContext();
    ctx.event().on("initialized", new MyContextEventListener());
    ctx.init();
}

Rune Funch Søltoft

unread,
Apr 10, 2016, 1:42:15 PM4/10/16
to object-co...@googlegroups.com
I might be of the view that emitting events outside the context breaks the context abstraction. Why does part of the interaction belong outside the context and not in one or several of the roles of the context?

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.

Matthew Browne

unread,
Apr 10, 2016, 2:29:05 PM4/10/16
to object-co...@googlegroups.com
I was thinking of a couple of possibilities. One of them is that a use case context could emit events and that an MVC controller would subscribe to those events. This might be useful for use cases involving more than one interaction with the user that can't be completed with a single synchronous call to a trigger method (like we were discussing in the library borrowing example). It would keep UI concerns out of the use case context so that the use case could potentially hook into different UIs (thanks to the decoupling), and I think it would make the use case context more focused, and thus more readable.

My other ideas about where this would be used are more vague...I thought of the possibility of an event-emitting Controller as a Context. But I need to think about this more. After all, the design should drive the choice of tools, not the other way around. Question for you and the group: do you find that in your DCI code, you have much need for writing event-driven code, e.g. a pub/sub mechanism?

Matthew Browne

unread,
Apr 10, 2016, 2:31:36 PM4/10/16
to object-co...@googlegroups.com
P.S. I originally read your reply as a criticism of the whole idea of a Context emitting events that external objects subscribe to. Or did you just mean that you favor my second code example over the first?

Rune Funch Søltoft

unread,
Apr 10, 2016, 4:10:53 PM4/10/16
to object-co...@googlegroups.com
I view events as one part of an interaction saying "whoever I'm interacting with do stuff" I'd prefer two roles agreeing to interact rather than the more fire and forget approach of events. What's the gain of one of the roles in the interaction being unnamed/outside the context? Or put differently what's the gain of an interaction not being contained in a context? and is that then DCI?

So in you concrete example of MVC what stops you from having an MVC context? Or is the general consensus that Model, View and Controller are not interacting?


Mvh
Rune

Matthew Browne

unread,
Apr 10, 2016, 4:36:15 PM4/10/16
to object-co...@googlegroups.com
On 4/10/16 4:10 PM, Rune Funch Søltoft wrote:
I view events as one part of an interaction saying "whoever I'm interacting with do stuff" I'd prefer two roles agreeing to interact rather than the more fire and forget approach of events.
That is indeed a weakness of events - overuse of events can certainly make code harder to understand. An advantage is that multiple objects can be notified of an event without having dependencies on the listening objects and having to manually notify each of them. But whether or not that's a real advantage in DCI code is a good question. One place where I think events are definitely necessary is in the UI - that's essential complexity, as users can interact with the computer in multiple ways in different sequences (to the extent that the UI allows that).

What's the gain of one of the roles in the interaction being unnamed/outside the context? Or put differently what's the gain of an interaction not being contained in a context? and is that then DCI?

So in you concrete example of MVC what stops you from having an MVC context? Or is the general consensus that Model, View and Controller are not interacting?
They're definitely interacting, but is taking part in an interaction sufficient to qualify as a role? I used to think so but now I'm not so sure. To take a Controller for example, how would you determine which methods are instance methods and which are role methods? I'm guessing those methods that interact with other objects (such as views) would be the role methods, but isn't the purpose of every method of a controller to interact with other objects (except for its private methods)? Another thing to consider with regard to roles is genericity (as Cope has been reminding us). Could you substitute a different Model, View, and Controller in the same MVC Context and still have it work correctly? I haven't tried it and maybe the answer is yes; it's an interesting question to consider... I plan to do some experiments with this so stay tuned, but in the meantime feel free to share any other thoughts.

Hai Quang Kim

unread,
Apr 10, 2016, 10:01:03 PM4/10/16
to object-composition
I am using DCI with existing system (game engine).
So I am not sure what is the right way yet.

But below is how I use DCI + MVC

I have a framework, ecosystem that allows me to create new entity with components. ( eg. a car entity has components: physics, visual, brain...)
(Entity is a container with predefined code, components are where I can add custom code).

For each entity's component the framework give me events: on created, on deleted, on update....
I also can have one component dispatching events and other components listen to that event.

I treat all Components as Controller (tool, framework, wiring...)

On those events, I trigger context. So context has no thing to do with events. 
Events are triggers and entry points for context. Context may have many entry points 
(not sure it is the right way, just to avoid too many contexts, like one context per event...)

I am thinking about electric system in my house:

- The wiring under the wall is the tool, framework.
- When I plug in my TV, sound, mic... they are Roles in Context of Karaoke :)

So in my Component code I only focus on save load objects, setup context, wiring events to trigger Context.
(Simple controller, complicated context)

Compared this to my previous approach: all logic stays in the Component Code (big giant controller, as much as 10K LOC)

Still playing with idea and code.. but this is what I got so far.

(btw, if any one want to play with simple game engine: https://playcanvas.com )

I did the same thing for my WPF application (MVVM).
I keep my VM as simple as possible (events, data...) and put more logic into Context.

There are 2 kinds of  Contexts:
1- Application Contexts: mix framework objects + domain objects (MV, V, M)
2- Domain Contexts: only domain objects. (M)

Btw, with this approach I will focus on testing Contexts more than the controllers.
Model/Data may be simple enough to have very little unit test ( eg. Activity's late start time...)

/quang
To unsubscribe from this group and stop receiving emails from it, send an email to object-composition+unsub...@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.
--
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-composition+unsub...@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.

--
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-composition+unsub...@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.
--
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-composition+unsub...@googlegroups.com.

Rune Funch Søltoft

unread,
Apr 11, 2016, 2:08:29 AM4/11/16
to object-co...@googlegroups.com

> Den 10. apr. 2016 kl. 22.36 skrev Matthew Browne <mbro...@gmail.com>:
>
> An advantage is that multiple objects can be notified of an event without having dependencies on the listening objects and having to manually notify each of them
Are you propagating events with a UDP broadcast or something similar? Because if you are not I'd say (to provoke a different line of thought) that your model based in a class view is lying to you. The classes might not know each other but the objects will have to be connected either directly or through a bus, unless it's a broadcast but even in the case of a broadcast the objects are interacting and thus do depend on each other whether or not their classes do so

Egon Elbre

unread,
Apr 11, 2016, 2:18:34 AM4/11/16
to object-composition
I do not understand what actual problem you are trying to solve.

I would solve the example as :)

context MyContext {
    public MyContext() {}
    public void init() {}
}

{
    MyContext ctx = new MyContext();
    ctx.init();
    System.out.println("initialization occurred");
}

// Over time I've realized that you rarely get a good solution from an abstract problem.

+ Egon

Egon Elbre

unread,
Apr 11, 2016, 2:22:12 AM4/11/16
to object-composition
Do you have the code uploaded somewhere?

It looks like it's too complicated for what it does.

+ Egon

Matthew Browne

unread,
Apr 11, 2016, 9:49:24 AM4/11/16
to object-co...@googlegroups.com
On 4/10/16 10:01 PM, Hai Quang Kim wrote:
On those events, I trigger context. So context has no thing to do with events. 
Events are triggers and entry points for context. Context may have many entry points 
(not sure it is the right way, just to avoid too many contexts, like one context per event...)
I am still mulling over what Cope said about this approach:

On Thursday, February 11, 2016 at 5:27:06 AM UTC-5, cope wrote:

Den 10/02/2016 kl. 23.44 skrev Matthew Browne <mbro...@gmail.com>:

I still think that using trigger methods as in the shopping cart example is a valid approach, but I admit it doesn't enforce that the programmer call the trigger methods in the correct order. 

Remember, the whole goal of DCI is to be able to analyze the code to see that it works correctly. Splitting the design along slices of the use case and outboarding the sequencing to something external to the Context breaks this completely. It of course can work and is kind of the way we’ve been doing things all these years. But it’s about as un-DCI-like as you can get.

However, I haven't yet found a solutions I'm satisfied with that both keeps a single Context for the overall use case (which could either delegate some behavior to nested contexts, or not) and doesn't make the MVC controller part of that Context (which introduces additional coupling between the use case and the UI).

My main reason for wanting reusable event-emitting behavior is to help facilitate communication between MVC components and use case Contexts. I was envisioning Controllers subscribing to events emitted by Contexts and/or Contexts subscribing to events emitted by Controllers. I am thinking this might help with the sequencing problem if done carefully (but if not done well it also runs the risk of making things worse). And of course, a well-known pattern is for the Model to emit events and for the Views to subscribe to those events (the "synchronize model and view" pattern). So if event subscription/emitting functionality is needed in more than one place, I think it makes sense to give it its own Context that could be used something like a trait or mixin (although Contexts don't have traits, so I tried forwarding/delegation instead).

I am intrigued by Rune's idea of an MVC Context (even aside from the particular challenge of multi-interaction use cases) so I am going to experiment with that. The Model, View, and Controller obviously interact, and the Controller and Model interact with the Use Case Context, so if managing these interactions in a sort of central "hub" is feasible I think that will be a good thing.

Anyway, enough talk from my end for now; I've gotten a bit ahead of myself...

Trygve Reenskaug

unread,
Apr 11, 2016, 11:21:09 AM4/11/16
to object-co...@googlegroups.com
The fundamental idea of MVC is to separate the code for the substance (i.e., the Model) from the I/O. There could be contexts for the operations on the model and separate contexts for the IO (View/Controller). You can find an example of this separation in my big planning example. A single context that combines the M and the V/C behaviors would violate this idea.

Rune Funch Søltoft

unread,
Apr 11, 2016, 11:36:05 AM4/11/16
to object-co...@googlegroups.com

> Den 11. apr. 2016 kl. 17.21 skrev Trygve Reenskaug <try...@ifi.uio.no>:
>
> A single context that combines the M and the V/C behaviors would violate this idea
Are you saying that a context where the model, view, controller each play their part would violate MVC? If so why?

Matthew Browne

unread,
Apr 11, 2016, 11:39:22 AM4/11/16
to object-co...@googlegroups.com
To echo what Rune said, I think the idea is not to mix the M and V/C
behavior in one place, but rather to use an MVC Context only to
facilitate the interaction between these components. (For example, all
the Model code would still be in data classes and use case contexts.)

Trygve Reenskaug

unread,
Apr 11, 2016, 12:03:37 PM4/11/16
to object-co...@googlegroups.com
No. I say that The fundamental idea of MVC is to separate the code for the substance (called the Model) from the from the presentation to the user of the information hidden in that model. Therefore, it could be contexts for the operations on the model and separate contexts for the presentation to the user (View/Controller). You can find an example of this separation in my big planning example. A single context that combines the M and the V/C behaviors would violate this idea.

Contexts that define how the model works will, of course, include a role that somehow represents the Model.  Contexts that define how the Model data are transformed into something that the user can relate to will, of  course, include a role that somehow represents the Model.

I don't know if this helps. Language is hard. Misunderstandings appear in the most surprising places.

James O Coplien

unread,
Apr 11, 2016, 12:28:05 PM4/11/16
to object-co...@googlegroups.com

Den 10/04/2016 kl. 22.36 skrev Matthew Browne <mbro...@gmail.com>:

 One place where I think events are definitely necessary is in the UI - that's essential complexity, as users can interact with the computer in multiple ways in different sequences (to the extent that the UI allows that).

I know that is the common convention and certainly the current fashion, but X used to have a choice between doing things in a directed or event-driven way. All of my early interfaces were of that nature.

James O Coplien

unread,
Apr 11, 2016, 12:58:49 PM4/11/16
to object-co...@googlegroups.com

Den 11/04/2016 kl. 15.49 skrev Matthew Browne <mbro...@gmail.com>:

However, I haven't yet found a solutions I'm satisfied with that both keeps a single Context for the overall use case (which could either delegate some behavior to nested contexts, or not) and doesn't make the MVC controller part of that Context (which introduces additional coupling between the use case and the UI).

The current trygve environment solves one important subset of this situation that I think is food for thought.

The trygve environment supports a blocking event wait primitive called InputStream.read… I can construct an InputStream from a Panel and then, from within a Context method, invoke the read method on the resulting InputStream object. That call will block until it is unblocked by an input event (in particular, by a KEY_RELEASE event).

Alternatively you can create a Panel and override its handleEvent method, with the option of directing the event handling to any object you want, including a Context instance.

The blocking read solution admits an “event wait” state in the Context, but the fact that it is inline in the code means that I can reason about it within the Context as a component of system state. The code flows naturally.

To me, it seems natural to use the handleEvent solution as a trigger to create and enact a Context. The handling of an event should be a system use case in its own right. Without the blocking, it can come at any time during execution. That means it is decontextualized, so handling it within a Context presumes some kind of inappropriate knowledge about the system state at that point. At best it can set a flag or just deposit event data and exit (as in the pong example).

The reason I find the pong example interesting is that it is best viewed, I think, as several use cases. The ball’s path is one use case; the paddle’s position is a parallel (sic.) use case which ideally would block on input events, but does not, because trygve is single-threaded in the end-user computational model it provides (though it has three threads internally).

rune funch

unread,
Apr 11, 2016, 1:11:06 PM4/11/16
to object-co...@googlegroups.com
Hi Trygve,
Can you explain why you think that the connection between model and view/controller may only be present at run time and not explicitly shown in code? Ie why you don't think it can be explicitly shown in a context but needs to use some other mechanism that is not as readily available to the reader of the code

Best regards
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.

rune funch

unread,
Apr 11, 2016, 1:16:55 PM4/11/16
to object-co...@googlegroups.com
2016-04-11 18:58 GMT+02:00 James O Coplien <co...@gertrudandcope.com>:
The reason I find the pong example interesting is that it is best viewed, I think, as several use cases. The ball’s path is one use case; the paddle’s position is a parallel (sic.) use case which ideally would block on input events, but does not, because trygve is single-threaded in the end-user computational model it provides (though it has three threads internally)

My model of an OO system has one object. That object can consist of a network of OO systems. in this case it might consist of three each playing their part and how they communicate with each other is defined by the one object called "the system". 

James O Coplien

unread,
Apr 11, 2016, 1:30:54 PM4/11/16
to object-co...@googlegroups.com

Den 11/04/2016 kl. 19.16 skrev rune funch <funchs...@gmail.com>:

My model of an OO system has one object. That object can consist of a network of OO systems. in this case it might consist of three each playing their part and how they communicate with each other is defined by the one object called "the system". 

No disagreement — but how many program counters are there?

Trygve Reenskaug

unread,
Apr 11, 2016, 3:44:04 PM4/11/16
to object-co...@googlegroups.com
I don't understand the question. Which part of the program is not shown in the code? Concrete example please.

Matthew Browne

unread,
Apr 11, 2016, 5:56:47 PM4/11/16
to object-co...@googlegroups.com
On 4/11/16 12:58 PM, James O Coplien wrote:
The ball’s path is one use case; the paddle’s position is a parallel (sic.) use case which ideally would block on input events, but does not, because trygve is single-threaded in the end-user computational model it provides (though it has three threads internally).
Why would blocking on input events be ideal? A useful lesson I've learned from node.js is that blocking a thread while waiting for an event is inefficient. Of course, in most cases it would be premature optimization to worry about this (node.js is, after all, concerned with massive scalability of incoming web requests). But even aside from the performance implications, I'm curious why you say it would "ideally" block on input events - because it keeps the code simpler? (There are ways to make asynchronous code more readable, but they would complicate the underlying trygve environment quite a bit, so if that's your concern then you're probably right that the complexity isn't worth it in the case of trygve.)

Rune Funch Søltoft

unread,
Apr 12, 2016, 12:01:01 AM4/12/16
to object-co...@googlegroups.com


> Den 11. apr. 2016 kl. 21.44 skrev Trygve Reenskaug <try...@ifi.uio.no>:
>
> I don't understand the question. Which part of the program is not shown in the code? Concrete example please.
I can hardly create a meaningful example of something that's not there :). But might let's try with a series of questions. How are you connecting the model and view/controller?

James O Coplien

unread,
Apr 12, 2016, 2:19:46 AM4/12/16
to object-co...@googlegroups.com
I am putting the performance issue aside for now — as for all things trygve-related. In trygve there is very little difference between the blocking implementation of read and the handling of an event.

The issue is code readability. To read a character from a graphical input entails an event at some level. Using the read API pushes the event behind the language’s abstraction boundary and allows me to link it to the computations leading up to it, and to those following it, in the same Context and even in the same method.

If I raise the event handling to the context level (handleEvent) then by necessity there is a context switch. If that event is part of the use case (for a common-sense interpretation of whatever that means) then the use case is no longer a physically contiguous block of code: I need to understand at least two methods to understand the flow. With the read implementation I in theory can do it in a single method. It would be different if we had FORTRAN entry points or coroutine syntax, but such syntax is pretty scarce in modern languages.

If I instead use read to read a single character, then the point of “interruption” is well-demarcated, and there is no exceptional handling of the flow. It is a first step of turning time into space (where “space” is the linear dimension of the code text). Functional programming takes this a step further by viewing method calls the same way I view events and just currying them away by spatially connecting them.

There is a longstanding fundamental dichotomy here that the use case people have long known about, in that a use case can be synchronously or asynchronously invoked. One rule of thumb for asynchronously-invoked use cases is that the ensuing computation must remain local to the use case that it interrupts (though it may interrupt it at any point). I would guess that formal UML semantics enforce this. If we are to view our Contexts as implementations of UML system use cases, this is well-traveled territory.

I think there is a good discussion to be had here about how DCI and time relate. The read example is instructive because it turns an event-driven solution into something that is syntactically sequential, in line with the presumed sequential model we’re used to in current DCI implementations. The question is: can we do this trick with all events? And should this be a part of the DCI computational model? I have already found it to be an essential part of the trygve virtual machine architecture (though we still need to sort out how much of it is due to Java’s sophomoric approach to design).

Trygve Reenskaug

unread,
Apr 12, 2016, 2:21:38 AM4/12/16
to object-co...@googlegroups.com
As I do it in the big planning example.
http://fulloo.info/Examples/SqueakExamples/BB9Planning/index.html
Also see a discussion of the same example in
http://fulloo.info/Documents/CommSenseCurrentDraft.pdf
You will find discussions about its 2-dimenaional system architecture and how it connects the model and view/controller. 
If you don't fully grok MVC, you could read
http://folk.uio.no/trygver/2003/javazone-jaoo/MVC_pattern.pdf

Egon Elbre

unread,
Apr 12, 2016, 2:51:19 AM4/12/16
to object-composition


On Tuesday, 12 April 2016 00:56:47 UTC+3, Matthew Browne wrote:
On 4/11/16 12:58 PM, James O Coplien wrote:
The ball’s path is one use case; the paddle’s position is a parallel (sic.) use case which ideally would block on input events, but does not, because trygve is single-threaded in the end-user computational model it provides (though it has three threads internally).
Why would blocking on input events be ideal? A useful lesson I've learned from node.js is that blocking a thread while waiting for an event is inefficient. Of course, in most cases it would be premature optimization to worry about this (node.js is, after all, concerned with massive scalability of incoming web requests).

This is only because of internals of node.js. AFAIR, Go overhead in file blocking is around 200 instructions + some memory traffic (can't find the reference so might be wrong).


+ Egon

Matthew Browne

unread,
Apr 12, 2016, 8:38:01 AM4/12/16
to object-co...@googlegroups.com
On 4/12/16 2:19 AM, James O Coplien wrote:
I think there is a good discussion to be had here about how DCI and time relate. The read example is instructive because it turns an event-driven solution into something that is syntactically sequential, in line with the presumed sequential model we’re used to in current DCI implementations. The question is: can we do this trick with all events? And should this be a part of the DCI computational model? I have already found it to be an essential part of the trygve virtual machine architecture (though we still need to sort out how much of it is due to Java’s sophomoric approach to design).
I'm sure the answer will be multi-faceted, but just to put in my two cents, I think a keyword like "await" or "yield" is helpful for asynchronous code because it clearly indicates that the code will have to wait for something but doesn't lead to complications like nested callbacks. (See, for example, the await keyword in C#.)

There are some cases where I think it makes sense for the asynchronous or blocking nature of an operation to be transparent to the programmer (i.e. happen behind the scenes and look synchronous on the surface), and I think InputStream.read() is a good example of that. But in other cases (maybe just in user-land code), some syntax to explicitly differentiate between synchronous and asynchronous method calls could add clarity. Whether or not it makes sense to add such a thing to the trygve language in particular is another question. BTW, another thing I've been wondering is whether we/you might consider adding first-class functions to trygve.

And of course I have only commented on one aspect of your question and only at the technical level. I look forward to more discussion about the big picture.

Matthew Browne

unread,
Apr 13, 2016, 12:20:02 AM4/13/16
to object-co...@googlegroups.com
Hi everyone,
I wrote some (non-compiling) trygve code as an experiment of an MVC context (Rune's idea, although he may have had a very different implementation in mind). For now, just consider it one step beyond pseudo-code, just to share the idea with you all. I thought I'd start with something simple - a counter that can either be controlled via a slider, or incremented with an "Increment" button. Maybe it's too simple to give any realistic insights but it's a place to start.

I'm not sold on this idea, and I have a number of questions about things I think I've done incorrectly, but with that slew of caveats out of the way here's some code... I think the main question is whether pursuing this approach would ultimately yield more readable code than direct or event-based communication between Model, View, and Controller. In other words, is there anything here that potentially could improve on traditional MVC?

~~~

context CounterMVC {
    public CounterMVC(CounterObject counter) {
        Controller = this;
        Window = new CounterPanel();
        CounterModel = counter;
        CounterSliderView = new CounterSliderView(Window);
        IncrementButtonView = new IncrementButtonView(Window);
    }
   
    public void start() {
        Window.render();
        CounterSliderView.render();
        IncrementButtonView.render();
    }
   
    role Controller {
        public void incrementCounter() {
            CounterModel.increment();
            CounterSliderView.refresh();
        }
       
        public void decrementCounter() {
            CounterModel.decrement();
            CounterSliderView.refresh();
        }
       
        public void notifyModelChanged() {
            CounterSliderView.syncWithModel();
        }
    }
   
    role Window {
        public void handleEvent(Event event) {
            if (event.id == Event.MOUSE_MOVE) {
                int direction = CounterSliderView.determineMovementDirection(event);
                if (direction == 1) {
                    Controller.incrementCounter();
                }
                else if (direction == -1) {
                    Controller.decrementCounter();
                }
            }
            else if (IncrementButtonView.wasPressed(event)) {
                CounterModel.observableIncrement();
            }
            return true;
        }
    }
    requires {
        void render();
        void refresh();
    }
   
    role CounterSliderView {
        public void syncWithModel() {
            updateCounterVal(CounterModel.counter());
        }
    }
    requires {
        int determineMovementDirection();
        void updateCounterVal(int counter);
        void refresh();
    }
   
    role IncrementButtonView {} requires {
        void render();
    }

    role CounterModel {
        //temporary solution; direct language support for observable properties
        //or method interception would be best
        public void observableIncrement() {
            increment();
            Controller.notifyModelChanged();
        }
    }
    requires {
        public void increment();
        public void decrement();
        public int counter();
    }
}

/* NOTE: This is just for experimentation purposes and doesn't work like a proper slider.
 * It just responds to *any* horizontal mouse movement within the panel.
 */
class CounterSliderView {
    public CounterSliderView(CounterPanel panel) {
        panel_ = panel;
    }
    private CounterSliderView() {}
   
    public void render() {
        panel_.setForeground(Color.blue);
        panel_.drawRect(offset_.clone, 50, 10, 10);
        panel_.refresh();
    }
   
    public void updateCounterVal(int counter) {
        panel_.removeAll();
        panel_.drawRect(offset_ + counter.clone, 50, 10, 10);
        panel_.refresh();
    }
   
    //Returns 1 for forwards, -1 for backwards, or 0 for no (horizontal) movement
    public int determineMovementDirection(Event mouseMoveEvent) {
        int direction;
        if (event.x > prevX_) {
            direction = 1;
        }
        else if (event.x == prevX_) {
            direction = 0;
        }
        else direction = -1;
       
        prevX_ = mouseMoveEvent.x.clone;
        prevY_ = mouseMoveEvent.y.clone;
        return direction;
    }
   
    private panel_ panel;
    private int offset_ = 50;
    private int prevX_ = 0;
    private int prevY_ = 0;
}

class IncrementButtonView {
    public IncrementButtonView(CounterPanel panel) {
        panel_ = panel;
    }
    private IncrementButtonView() {}
   
    public void render() {
        panel_.drawOval(x_, y_, radius_, radius_);
        panel_.drawString(x_ + radius_ - ((label_.length() * 6) / 2), y_+ radius_ + 6 , label_);
        panel_.refresh();
    }
   
    //TODO
    public boolean wasPressed(Event event) {
    }
   
    private String label_ = "Increment";
    private int x_ = 70,
                y_ = 70,
                radius_ = 40;
}

class CounterPanel extends Panel {
    public CounterSliderView() {
        Panel();
    }

    public void render() {
        frame_  = new Frame("Counter");
        frame_.add("Center", this);
        frame_.resize(600, 400);
        frame_.setVisible(true);
    }

    public void refresh() {
        redraw();
    }
   
    private Frame frame_;
}

class CounterObject {
    public void increment() {
        counter_++;
    }
    public void decrement() {
        counter--;
    }

    private int counter_ = 0;
    public int counter() {
        return counter_;
    }
}

{
    CounterObject myCounter = new CounterObject();
    new CounterMVC(myCounter).start();
}

Egon Elbre

unread,
Apr 13, 2016, 3:23:01 AM4/13/16
to object-composition
On Wednesday, 13 April 2016 07:20:02 UTC+3, Matthew Browne wrote:
Hi everyone,
I wrote some (non-compiling) trygve code as an experiment of an MVC context (Rune's idea, although he may have had a very different implementation in mind). For now, just consider it one step beyond pseudo-code, just to share the idea with you all. I thought I'd start with something simple - a counter that can either be controlled via a slider, or incremented with an "Increment" button. Maybe it's too simple to give any realistic insights but it's a place to start.

I'm not sold on this idea, and I have a number of questions about things I think I've done incorrectly, but with that slew of caveats out of the way here's some code... I think the main question is whether pursuing this approach would ultimately yield more readable code than direct or event-based communication between Model, View, and Controller. In other words, is there anything here that potentially could improve on traditional MVC?

Here's what an immediate mode approach would look like:

context Application {
    Application(UI ui, Counter counter) {
        UI = ui;
        Counter = counter;
    }

    role UI {
        void do(){
            Counter.slider(new Rect(0, 10, 100, 10));
            Counter.button(new Rect(0, 30, 100, 10));
            Counter.label(new Rect(0, 50, 100, 10));
        }
    } requires {
        Boolean button(String text, Rect rect);
        Integer slider(Rect rect, Integer min, Integer max, Integer value);
        void    text(String text);
    }

    role Counter {
        void slider(Rect rect){
            this.setValue(UI.Slider(rect, 0, 100, this.value()));
        }
        void button(Rect rect){
            if(UI.button("Increment", rect)){
                this.setValue(this.value() + 1);
            }
        }
        void label(Rect rect){
            UI.label(this.value().toString() rect)
        }
    } requires {
        Integer value();
        void setValue(Integer v);
    }
}

James O Coplien

unread,
Apr 13, 2016, 3:58:46 AM4/13/16
to object-co...@googlegroups.com
This is not a question of syntax. It is a question of the computational model and of the architecture of the object machine. Does it have both user stacks and a kernel stack? Does each Context object have its own program counter?


--
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.

Matthew Browne

unread,
Apr 13, 2016, 8:32:34 AM4/13/16
to object-co...@googlegroups.com
Hi Egon,
I think your version is simpler mainly because it doesn't show the rendering code (graphics in trygve are currently pretty low-level), but it looks like this version also lacks any updating from the Model to the View, i.e. it would not update the slider position when the Increment button is pressed. Putting UI methods in the Counter role is interesting - the model object playing the role of a View (actually 2 Views). Anything else you were aiming to demonstrate?

Matthew Browne

unread,
Apr 13, 2016, 8:44:31 AM4/13/16
to object-co...@googlegroups.com
On 4/13/16 3:58 AM, James O Coplien wrote:
> Does each Context object have its own program counter?
Is that how it would work if we were following the actor model and
considering each Context to be an actor?

Egon Elbre

unread,
Apr 13, 2016, 8:59:26 AM4/13/16
to object-composition
On Wednesday, 13 April 2016 15:32:34 UTC+3, Matthew Browne wrote:
Hi Egon,
I think your version is simpler mainly because it doesn't show the rendering code (graphics in trygve are currently pretty low-level), but it looks like this version also lacks any updating from the Model to the View, i.e. it would not update the slider position when the Increment button is pressed. Putting UI methods in the Counter role is interesting - the model object playing the role of a View (actually 2 Views). Anything else you were aiming to demonstrate?

Maybe you haven't seen how immediate mode works?

In the simplest version you could imagine running "UI.do" every 13ms; in more complicated case, when you have change in the user input states. The "required" methods on the UI do two things the interaction logic and render.

So when the user presses down and releases, the UI.do is triggered, and the code UI.button would draw the button, check whether mouse was clicked, returns true and finally enters the specific block.

if(UI.button("Increment", rect)){
    this.setValue(this.value() + 1);
}

And finally the UI is updated, with UI.do.

The most basic UI.button implementation would look like:

context UI {
    role Display { ... }
    role Mouse { ... }
    role Keyboard { ... }
    
    Boolean button(Rect rect, String text) {
        Display.fillRect(rect, Style.ButtonBackground);
        Display.strokeRect(rect, Style.ButtonBorder);
        Display.drawText(rect, text, Style.ButtonText);   
        return (Mouse.leftClicked() && Mouse.position.inside(rect)) || Keyboard.pressed(Key.Enter);
    }
    ...
}

More information on the approach:

(Of course, not a silver bullet.)

+ Egon

Matthew Browne

unread,
Apr 13, 2016, 9:17:40 AM4/13/16
to object-co...@googlegroups.com
On 4/13/16 8:59 AM, Egon Elbre wrote:
> Maybe you haven't seen how immediate mode works?
No I hadn't; thanks for the explanation. Of course for the more
sophisticated version you mentioned (running UI.do() "when you have
change in the user input states") the interaction code would be a bit
more complex, and probably have more in common with MVC.

Egon Elbre

unread,
Apr 13, 2016, 9:27:31 AM4/13/16
to object-composition
ui = new UI(getKeyboardState(), getMouseState());
app = new App(ui);

while(true){
Keyboard newKeyboard = getKeyboardState();
Mouse newMouse = getMouseState();

if(ui.Keyboard.equal(newKeyboard) && ui.Mouse.equal(newMouse)){
sleep(13);
continue;
}

ui.Keyboard = newKeyboard;
ui.Mouse = newMouse;

ui.Display.clearFrame();
app.do();
ui.Display.swapFrame();
}

// or

ui = new UI(getKeyboardState(), getMouseState());
app = new App(ui);

while(event = pollEvent()){
if(event instanceof MouseEvent) {
ui.Mouse = getMouseState();
// or, ui.Mouse.update(event);
}
if(event instanceof KeyboardEvent){
ui.Keyboard = getKeyboardState();
}

ui.Display.clearFrame();
app.do();
ui.Display.swapFrame();

Reply all
Reply to author
Forward
0 new messages