04-15-2013 07:33 AM
fabric wrote:
Ahh - I think I just realised something. In LapDog each message is given a "name" as part of the message data, right? That is not the "name" I was talking about... I was referring to the name of the message class itself, e.g. "hw.camera.AcquireImage.lvclass".
Yes, that is correct. (It's not quite right to say that's how it is done "in LapDog," but that is how I typically use it. It's more a reflection of the decision to use name-data messages instead of command-pattern messages. Both LapDog and the AF can use either style of message.) I name my message classes according to the type of data they carry. i.e. StringMessage, PathArrayMessage, etc. My message objects have a name assigned at runtime that maps to a specific message handler in the receiving actor.
I find this helps me create loosely coupled systems since most message classes can be dumped in a common library and used with any actor. It also help me keep the separation between the message data and what the receiving actor does with the data clear in my head.
The question is still relevant, but I'll set it aside for now and think about it for a while.
Tst wrote: You could try "neutral" or "inaction" instead of "benign". I would suggest "parve", but I get the feeling the number of people who would have an instinctual understanding of it would be somewhat limited.
I think your feelings are correct in this matter. "Inaction" isn't quite right either. "Neutral" could be a better term. I'll have to think on that for a while. Really, the most accurate term is "non-control" messages, but it's not very helpful. ("Inert" is close too, but it still carries the implication that nothing happens.)
Tst wrote: Do you even need the distinction? I see that you're struggling with defining the line between control and benign, and that's probably because there is no such hard line. Why not just treat all messages the same and let the reciever decide what the importance of each message is?
The actor receiving the messages, by virtue of its implementation, does decide the relative importance of each message it receives, but it doesn't necessarily know how that message or any actions it takes as a result of that message affect the (sub)system as a whole. I think you're correct that there probably is no hard line between control and benign, just like there's no hard line between an actor and common QMH implementations. And, even though lots of programs have been written without making this distinction, I think there's value in understanding the difference between them and thinking about your messages in this way. (Which is a completely subjective and unsupported conclusion based entirely on the perceived value I get from categorizing my messages this way.)
Shutdown messages are a type of control message. Coincidentally, lots of people have trouble getting their systems to shutdown correctly. This often occurs when the system uses a point-to-point messaging topology (as opposed to a tree topology) and the system needs some kind of controlled shutdown. Why is this such a problem? My theory is the developers do not recognize the difference between control and benign messages, or the importance of clearly managing control messages as they propogate through the system.
04-15-2013 08:39 AM
Daklu wrote:
Shutdown messages are a type of control message. Coincidentally, lots of people have trouble getting their systems to shutdown correctly.
Can you point to any other message which is universally required? I believe it was you yourself who posted somewhere not a week ago that this is the only real req for an actor.
The AF, incidentally, handles this universally by having the built-in stop message (which sets an error) and error handler (which knows to stop the actor when it encounters that error. It allows the user to override those, but that requires a conscious action. That said, I'm assuming that when you talk about a controlled shutdown, you're talking about something more complex which has to do with the relations between the actors and who stops when.
04-15-2013 12:15 PM
Thoughts on everything written thus far:
Me/You is confusing. Make them absolute coordinates, not relative. Have you ever gotten driving directions from someone who said the destination is on the "left" side of the street? What if I encounter construction on the way there and have to reroute to come from a different direction? [Edit] Probably a better example: It reminds me of learning how to use the Thevenin equivalent voltage of a network in school: the value changes whether you're "looking into" the network or "looking out of it". Figuring out which one was which and how to do the simplifications was always confusing as heck to me.
AF already provides target identification when sending any message by merit of the "caller queue" and "self queue" functions, as well as the standard practice of namespacing an actor's messages with a library (af_Broadcaster.lvlib:Register for Broadcast.lvclass). If the library is removed to help with dependency management, an actor's incoming messages are prefixed just like pre-lvlib code always was (broadcaster_Register for Broadcast.lvclass).
In AF, messages are associated with the actor that receives them, not with a set of actors or a subsystem. Naming a message after its role in the system makes refactoring and reuse much more difficult because that role might change.
04-23-2013 12:04 AM
Daklu wrote:
I name my message classes according to the type of data they carry. i.e. StringMessage, PathArrayMessage, etc. My message objects have a name assigned at runtime that maps to a specific message handler in the receiving actor.
And do you find yourself naming them "Three Integers One Boolean and An Instance of Animal.lvclass That Might Be A Child Instance.lvclass"? Because my messages very rarely have a single piece of data in them that would lend themselves to naming by the contents. Besides, the contents of a given message can change and should be one of the private aspects of the class. Just because the Send is done with two timestamps doesn't mean that the message sent contains two timestamps... it might contain an offset between those two times or somesuch. Naming based on the private contents seems like an incredibly bad idea.
04-23-2013 10:40 AM
AristosQueue wrote:
And do you find yourself naming them "Three Integers One Boolean and An Instance of Animal.lvclass That Might Be A Child Instance.lvclass"? Because my messages very rarely have a single piece of data in them that would lend themselves to naming by the contents.
Occasionally I do create messages with unique combinations of data types that are designed to work with a specific actor. In those cases I give the message class a meaningful name in the context of the subject actor and put it in the same library as the actor. (Often I give the message class the same name as the message handling case that processes it.)
AristosQueue wrote:
Besides, the contents of a given message can change and should be one of the private aspects of the class.
In name-data messaging (as opposed to command-pattern messaging) the contents of every instance of a message class will always be the data types it carries. What that data represents depends on the actor receiving the message.
The purpose of name-data messages is simply to transport data between actors. It only has two methods: Create (set data) and Destroy (get data.) In it's simplest form there's no point in having the message class maintain private data that isn't shared with the receiving actor.
AristosQueue wrote: Just because the Send is done with two timestamps doesn't mean that the message sent contains two timestamps... it might contain an offset between those two times or somesuch.
In my opinion this muddies the division of responsibilities, makes actors more complex than necessary, and makes it harder to reuse them in different situations. If your receiving actor is expecting a single timestamp representing an offset, the sending actor should be sending a single timestamp representing an offset. If the receiving actor expects a start time and end time, the sending actor should be sending a start time and end time.
Writing a message handler that accepts a start time and end time when the receiver is really interested in the difference is customizing the message handling code for the sender, and that increases the receiver's coupling to the sender. Have the message send the data the receiver is interested in. It's the sender's responsibility to translate its information into a context the receiver can understand.
(This all assumes the receiver is the subject of the message--a you message. It's a bit different if the sender is the subject of the message--a me message.)
AristosQueue wrote: Naming based on the private contents seems like an incredibly bad idea.
The purpose of naming message classes according to data type is that it encourages me to design my actor's message interface using preexisting message libraries. Instead of creating a custom 3dPoint message with fields for x, y, and z, I'd send the information as an Int32Array message from my existing message library and document the values contained in each element. That way the sender and receiver are statically linked to the library instead of to each other.
Yes, I do lose some of the type safety you get by creating custom types for each message. Keeping the actors loosely coupled is far more valuable to me than type safety.
---------
<deleted: duplication>
---------
I'll try to respond to Staab and tst soon.
04-23-2013 11:37 PM
Daklu wrote:
Yes, I do lose some of the type safety you get by creating custom types for each message. Keeping the actors loosely coupled is far more valuable to me than type safety.
I think that's the key insight I had when looking at all the apps that people were writing, leading up to the development of the Actor Framework: Actors were not interested in not knowing about their callers. They were much easier to design AND maintain when they said "my caller will have some interface like this" and then they implemented to assume that kind of caller. The caller already knows exactly what kind of actor he/she is calling, so there's already a static linkage in that direction. Having a low coupling linkage the other direction didn't introduce new dependencies. Would it be easier if LV had true interfaces? Oh heck yes! But in the vast majority of use cases, it doesn't seem to make a difference in the reusability of the actors to have single-inheritance used to give the caller the necessary interface. Really. Look at all your actors and see how much reusability you really get. The core contents of the actor are frequently reusued in other contexts. But the actor shell, which maintains the state, generally works for one particular kind of context only, and thus assumes an caller that is looking for that kind of shell.
In short, I agree that lower coupling can be achieved. I just don't see any empiric value to it. There's no return on investment for 90% of developers out there, so why jump through the hoops?
04-24-2013 08:23 AM
tst wrote: Can you point to any other message which is universally required? I believe it was you yourself who posted somewhere not a week ago that this is the only real req for an actor.
Nope, I can't. However, shutdown messages are not universally required either. What I said was, "the one decision every actor has to make is when to stop." It doesn't have to be triggered by a message and actors don't necessarily have to implement a stop message. All my actors are built to automatically shut down if either their receive queue or their caller's receive queue gets released. I consider these errors fatal. I have actors that are designed to shut down some amount of time after they are created. You could have an actor with a watchdog timer that needs to be reset by the caller.
tst wrote: That said, I'm assuming that when you talk about a controlled shutdown, you're talking about something more complex which has to do with the relations between the actors and who stops when.
Yep, when I'm doing a controlled shutdown I have to stop the actors in a specific order. Usually it's so I can continue to capture and monitor debug messages during the shutdown process. It's not hard to imagine physical systems that needs a controlled shutdown to prevent catastrophic failures.
David Staab wrote:Me/You is confusing. Make them absolute coordinates, not relative.
I admit I didn't think Me/You was confusing... until I came back and started answering Stephen's question. I had forgotten that Me/You is defined relative to the sender and ended up confused for a bit. Good call.
Is it more clear to define the subject of the message as simply the "sender" or "receiver?"
David Staab wrote:
Naming a message after its role in the system makes refactoring and reuse much more difficult because that role might change.
I'm not advocating naming the message according to its role in the system. Messages can be categorized according to their effect on the overall system (control/benign), but I strongly encourage message names to be defined from the point of view of the subject.
tst wrote:
The AF, incidentally...
AF already provides...
David Staab wrote:
I intended for this thread to be focused on AOD theory rather than any specific implementation of it. I don't mind discussing the various options wrt implementation and the tradeoffs between them--in fact I think it is appropriate to do so--but my gut feeling is these categorizations have value regardless of the framework used.
(Yes, I know this is an AF community. It has also become the de facto center for all things actor related in Labview. Until enough critical AOD mass accumulates in some other forum, I have to post here if I want to discuss something.)
04-24-2013 02:15 PM
AristosQueue wrote:
Actors were not interested in not knowing about their callers. They were much easier to design AND maintain when they said "my caller will have some interface like this" and then they implemented to assume that kind of caller.
I can't say whether or not my actors are interested in knowing about their callers. I've asked them, but they haven't answered yet.
I can say *I'm* not interested in having actors know about their callers. Creating messages so the receiver is always the subject (which is essentially what you're suggesting) is a valid way to assign ownership of each message's interface to an actor. Personally I find it much harder to design and maintain a system where all messages are receiver-subject compared to a system that uses both receiver-subject and sender-subject messages. Why? There are several reasons, but I'll briefly explain a couple of them here:
1. I architect from the top down, but generally implement code from the bottom up. I build small subsystems that are responsible for providing some small piece of functionality really well. Then I assemble them into bigger subsystems that are responsible for coordinating the activity of the smaller subsystems to provide a combined behavior that the smaller subsystems cannot provide on their own. Rinse and repeat until the app is complete. If all messages are receiver-subject, I cannot define the subsystem's messages that go up the chain of command until I know what effect the message will have on the caller, which means I need to know exactly how each event in each subsystem will impact the entire system. BDUF.
Introducing sender-subject messages for those messages that go up the chain of command allows me to create subsystems without any knowledge of their caller. I can, and usually do, write subsystems without knowing the exact details of how the application as a whole will use the information it provides. I make sure the subsystem provides all the information I expect will be needed, but the details get worked out as the project progresses and I develop the higher level subsystems. By sending information up the chain of command as sender-subject messages, I can have the subsystem implemented, tested, and checked in before even starting the higher level subsystem, and be reasonably sure I will not have to make any modifications to it.
2. When looking at any single actor, I have found one of the biggest obstacles to understanding how it contibutes to the application's overall behavior is figuring out who sent a message and why the message was sent. When all messages are receiver-subject, all I know from looking at that actor is I'm getting a bunch of requests from other actors to do stuff. I have no idea why or under what conditions those requests are being sent. You could argue the actor shouldn't care why or under what conditions the message is being sent. I agree, the actor shouldn't care. But the developer does care, because that's the information we need in order to understand the system as a whole.
Receiving sender-subject messages from sub actors makes the why immediately apparent. Imagine an actor interfacing with a UI sub actor and consider of the difference between receiving a message named "StartButtonPressed" (sender-subject) and the exact same message with the name changed to "StartTest" (receiver-subject.) Why was the StartButtonPressed message sent? Probably because the Start button was pressed. Why was the StartTest message sent? Hmm... I'm not sure. I'd better dig into the UI code and figure it out. Better yet, I have to do that every time I forget why the StartTest message was sent.
---------
When you say it's "easier" to design and maintain a system that only uses receiver-subject messages, I believe you. The question is, easier than what?
I believe, based both on my past code and on what I've seen in other's code, most developers don't adequately assign the responsibilities between any two components that interact with each other. It doesn't matter if the components are sub vis, classes, actors, or something else. Responsibility for defining the interface (con pane, message semantics, etc.) is one of those things that needs to be assigned. When the developer doesn't have a clear idea of who controls the interface definition, code changes resolving interface mismatches could be implemented anywhere. Maybe the caller changes, maybe the callee changes, maybe the information is routed through some translator external to both of them. I believe it's the unclear assignment of responsibility that makes the code hard to understand and maintain.
A pure receiver-subject system is one way to assign responsibility for defining the interface, and it is certainly an improvement over a system where the interface isn't owned by anyone. For me, receiver-subject systems don't work well because of the reasons I listed above. We can also imagine a system created purely with sender-subject messages. In this system no actor ever sends a request to another actor; they just send messages about stuff that happens to them. Semantically everything in the application is driven by events. This is also an improvement over systems where responsibility for the interface isn't assigned.
I use a mix of receiver-subject and sender-subject messages in my systems. With a hierarchical messaging topology sending receiver-subject messages down the tree and sender-subject messages up the tree works extremely well for me. It has been, thus far, so clearly superior to pure receiver-subject and pure sender-subject systems that I no longer bother even considering those paradigms. (Which may by my loss.)
I believe pure receiver-subject and pure sender-subject (sub)systems are probably preferable to mixed-subject systems in certain situations. I don't yet know what those situations are and I'm interested in exploring it further so I can understand when they should be used. I completely reject the assertion that pure receiver-subject systems are universally easier to design and maintain than pure sender-subject systems or well designed mixed-subject systems.
AristosQueue wrote:
Look at all your actors and see how much reusability you really get. The core contents of the actor are frequently reusued in other contexts. But the actor shell, which maintains the state, generally works for one particular kind of context only, and thus assumes an caller that is looking for that kind of shell.
I suspect you and I scope our actors differently. My higher level actors are usually written for one specific application and don't have much reusability. Lower level actors are written to provide some general functionality and are easily reused (or are on a trajectory towards reusability.)
AristosQueue wrote:
In short, I agree that lower coupling can be achieved. I just don't see any empiric value to it. There's no return on investment for 90% of developers out there, so why jump through the hoops?
This is kind of an aside to categorizing messages, but...
My response to this draws on my experience in strategy gaming. When designing a strategy game for longevity, you need to balance it around the strategies employed by the best players, not the strategies employed by the most players. Why? Because eventually the best player's strategies will propagate through the player community, and if the game isn't balanced around those strategies it will no longer be an interesting game for those players who learn them. The game can't satisfy their requirement, so they quit playing and move on to a different game.
I take a similar view with API design. If an API is to have longevity, it has to be designed so the most advanced developers can accomplish what they want to do, not just in a way that works for the average developer. Why? Because eventually those average developers will learn the things the advanced developers know and if the API cannot adequately satisfy their needs it ceases to be useful to them. They quit using it and start looking for something else that does satisfy their needs. Unfortunately, while the gamer can simply move on to another game, the developer likely still has acres of legacy code based on what he now considers an inadequate API.
Saying there's no ROI for most users so it's not worth spending time on it is a completely valid business decision. It seems to be a common one behind nearly all of NI's APIs. It doesn't take much to realize it also prioritizes the market segment over the individual and ignores how the individual's requirements change as they learn and grow. If NI wants to continue ignoring their advanced users that's not my decision to make. I will say I find the decision extremely disappointing... and a bit confusing given that NI says they care about the advanced user community.
[Edit - I originally interpreted the above quote as a justification for your design decisions for the AF, and my response is directed at that interpretation. Upon rereading it this morning, I realized you may be saying 90% of the projects get no ROI from the decoupling so there's no point for the developer to put in extra work to achieve it. That's a slightly different, and slightly more complicated, question. If that's what you meant, or if I otherwise completely missed the point of what you were trying to say, ignore the above response and let me know.]
04-25-2013 05:44 PM
Daklu wrote:
[Edit - I originally interpreted the above quote as a justification for your design decisions for the AF, and my response is directed at that interpretation. Upon rereading it this morning, I realized you may be saying 90% of the projects get no ROI from the decoupling so there's no point for the developer to put in extra work to achieve it. That's a slightly different, and slightly more complicated, question. If that's what you meant, or if I otherwise completely missed the point of what you were trying to say, ignore the above response and let me know.]
It is my totally annecdotal not scientific hypothesis that the vast majority of *projects* get no ROI from that level of decoupling. First level decoupling -- where you code to an interface and not to a specific instance -- is GREAT. Second level decoupling where you code to a generic pluggable system rarely seems to pay off. It is very valuable in some scenarios, but not nearly as many as some architects would have you believe by insisting on stretching for that ultimate decoupling.
Don't read me wrong -- you should decouple as much as you can. But the payback gets smaller, especially if you are a less experienced developer where doing that decoupling takes an excessive amount of time.
04-26-2013 12:48 AM
AristosQueue wrote: It is my totally annecdotal not scientific hypothesis that the vast majority of *projects* get no ROI from that level of decoupling. First level decoupling -- where you code to an interface and not to a specific instance -- is GREAT. Second level decoupling where you code to a generic pluggable system rarely seems to pay off. It is very valuable in some scenarios, but not nearly as many as some architects would have you believe by insisting on stretching for that ultimate decoupling.
I'm not sure what you mean by "that level of decoupling," and you appear to be under the impression I'm doing some huge amount of extra work to achieve it. The level of decoupling I shoot for is no circular dependencies. I'm not writing generic pluggable systems and I'm not stretching for ultimate decoupling. In fact, in my hierarchical actor trees the higher level actors typically are statically dependent on the lower level actors. (If I discover I do need to break the static dependency a higher level actor has on a lower level actor, it takes me maybe three minutes to create an abstract class, insert it in the class hierarchy, and implement the dependency injection in the higher level actor.)
Achieving this level of decoupling is not a huge amount of extra work. Yes, there is a little bit of overhead in managing the messages since they are more generic than command-pattern messages, but in the context of the entire project the additional time is insignificant. For me it amounts to listing all the public input (receiver-subject) and public output (sender-subject) messages along with the message class each message uses in a note next to the message handling loop, or in the vi's documentation if I've split that actor into its own class.
I actually agree most projects currently probably get little or no ROI from trying to increase decoupling. I suspect that's primarily because historically NI has emphasized type safety to the Labview community (for valid reasons), and when you're stuck in a type safety mindset decoupling is hard. There are two ways to improve ROI: Increase the value of the return or decrease the cost of the investment. The value of the return is completely decided by the developers working on the project. I can't influence that very much. I can reduce the cost of the investment and improve the ROI for those developers who, like me, have discovered that too much type safety is just as hard to deal with as too little type safety.
-------------
But that discussion is tangential to this thread. I'm curious about your thoughts on categorizing messages as control/benign or sender-subject/receiver-subject. Have I described them well enough for you to understand the differences I'm trying to highlight? If you put on your actor theory hat and think about actors and messages abstractly (i.e. without regard to a specific implementation) do you see how these ideas open the door to different ways of thinking about, organizing, and using actors? Are there other categorizations that could be useful?