Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

"Call Parent Method" not really consistent (to me)

I have noticed a strange and uncomfortable behavior of the "Call Parent Method":

In my actor cores before the parent actor is called and the (optional) helper loop is started I make some initializations.The VI for initialization is dynamic dispatch.

My problem is that the"Call Parent Method" calls the parent actor core, but initializes with the child initialization dynamic dispatch VI.

This way the parent actor may run totally differently because all dynamic dispatch VIs in it are running with the child implementation.

I attached a small example project, that contains only plain classes with dynamic dispatch initialization.

Just run "Actor_Core.vi" methods in a the classes to see the behavior.

I understand why it works this way, but it is not what I expect. I would expect everything in a "Call Parent Method" to step one level up on the hierarchy tree.

This behavior is not really consistent. If I have any code directly in the method that the "Call Parent Method" calls than it is executed normally. But as soon as I create a SubVI from a part of the code and make it dynamic dispatch, that part may not be the same any more. It will not be the same if I call it in a "Call Parent Method" and have an override for that. To me it doesn't feel like logical. I cannot use the modularity of creating SubVIs form a part of a VI without worrying that it may be executed totally differently.

What do you think about this behavior?

Did it ever hurt you in any project?

How do you get rid of this problem?

Edit: I used an incorrect name: "Call Parent Node". Replaced all those to: "Call Parent Method" (Message was edited by: komorbela)

0 Kudos
Message 1 of 11
(9,003 Views)

Personally, I consider this expected behavior and a useful feature of OO. Why make those subVI's dynamic dispatch if you don't want them to execute differently when a child class is carried on the wire?

Does anyone know how this works in other languages?

0 Kudos
Message 2 of 11
(4,995 Views)

To name a concrete use case: I made a template for all of my actors (in a specific project), and it is very handy to make the VIs in the template dynamic dispatch. This way when I change or create inheritance between my actors I don't have any conflicts.

And I ask your question together with you:

Does anyone know how this works in other languages?

0 Kudos
Message 3 of 11
(4,995 Views)

maxwellb wrote:

Does anyone know how this works in other languages?

This is how it works in *every* other OO language. It is, in fact, a rather important part of OO design. If a parent class is going to do work only on its own level, then it should be calling its own static dispatch methods, not dynamic dispatch methods. Calling a dynamic dispatch method is a signal that child classes have the ability to plug-in at this point in the algorithm with their own operations.

Komorbela: in your case, when you create subVIs, don't make them dynamic dispatch. Why are you doing that?

0 Kudos
Message 4 of 11
(4,995 Views)

AristosQueue wrote:

... This is how it works in *every* other OO language. It is, in fact, a rather important part of OO design. If a parent class is going to do work only on its own level, then it should be calling its own static dispatch methods, not dynamic dispatch methods.

I agree with what you say. If a child class is instantiated, it is perfectly good and expected to execute the child's version of the dynamic dispatch methods. I use this "feature" every day and understand it.

What I am unsure of is only the "Call Parent Method". If I use a method on a child object's wire from a parent class manually (I mean I put the VI there by my own hands (I mean mouse)) I know that dynamic dispatch VIs will execute on the child's level whenever applicable.

But when I use the "Call Parent Method" I have the following expectations:

  • As soon as parent method is called, the wire is converted to "parent type", or rather more "seen" so. I mean the child data is not cut of, just hidden.
  • Through the whole execution of the parent method, the dynamic dispatch VIs sense the wire as "parent type" and execute the parent version if applicable.
  • As the wire exits the parent method, the type is normal again and the child data is unhidden.

So, I feel that the "Call Parent Method" is like a temporary travel in class hierarchy. Obviously it is not so

I still feel like there should be a method that does what I expect. That method could be called "Call Parent Method Consistently" and it would call all SubVIs of the parent method in the same manner, like the "Call Parent Method" calls the parent method.

The question more exactly is: Does the "Call Parent Method" exist in other languages for dynamic dispatch VIs, and works the same way?

Komorbela: in your case, when you create subVIs, don't make them dynamic dispatch. Why are you doing that?

It was only an example to show why do I feel it is not really consistent. I don't do it.

However sometimes I do have an abstract base class with abstract methods. For example "Init", "Execute", "Deinit".

When I make children of it, the realizations of the abstract methods have to be dynamic dispatch.

And the problem comes when I make an abstract method in the base class involving the other abstract methods. For example: "FullCycle" containing:"Init" -> "Execute" -> "Deinit"

Calling any implementation of "FullCycle" from any child object would execute on the right hierarchy level, but if I call it with "Call Parent Method" it mixes the hierarchy levels.

I hope I made my point clear enough. There well may be no straightforward solution for my problem, but who knows...

P.s.: I have one recommendation to myself: Don't use dynamic dispatch SubVIs in dynamic dispatch VIs if you ever plan to use "Call Parent Method".

Any better advices?

0 Kudos
Message 5 of 11
(4,995 Views)

LVOOP always pays attention to the object on the wire, not the object you see in the IDE (they can be different).

Your object model is wrong for the functionality you are looking for.  Dynamic dispatch VIs ALWAYS respect the actual object type, not the wire type.  If you need different behaviour then as already stated, make the sub-VIs in the parent method static.

0 Kudos
Message 6 of 11
(4,995 Views)

komorbela wrote:

P.s.: I have one recommendation to myself: Don't use dynamic dispatch SubVIs in dynamic dispatch VIs if you ever plan to use "Call Parent Method".

Any better advices?

That is absolutely NOT a good rule. Use of dynamic dispatch subVIs in parent implementations of dynamic dispatch methods is critically important to many (most?) designs. You've learned the wrong lesson if that's what you come away with.

You've looked at the AF. Consider "Actor Core.vi". That's dynamic dispatch. Every actor calls up to its parent... the base implementation of Actor.lvclass:Actor Core.vi calls several dynamic dispatch subVIs -- Receive Message.vi, Handle Error.vi, Stop.vi [which is just a wrapper around the dyn disp call to Stop Core.vi].  Nothing would work if each of those calls did not dispatch to the correct core of the algorithm.

I think I have a better recommendation for you, but I put it at the end after discussing a couple of other points.

komorbela wrote:

The question more exactly is: Does the "Call Parent Method" exist in other languages for dynamic dispatch VIs, and works the same way?

Yes. And yes.

In C#, it is base.MethodName(). In JAVA, it is super.MethodName(). In C++, it is NameOfParentClass::MethodName(). In all cases, if the parent implementation of MethodName() included a call to another dynamic dispatch function, it would dispatch down to object's override of that subVI.

komorbela wrote:

  • As soon as parent method is called, the wire is converted to "parent type", or rather more "seen" so. I mean the child data is not cut of, just hidden.

The data is never hidden from the code. It is always there on the wire. It is simply that the parent class doesn't have any instructions that modify that data -- except for the methods that are dynamic dispatch that allow the child class to say how its data should be modified at that point in the algorithm.

komorbela wrote:

It was only an example to show why do I feel it is not really consistent. I don't do it.

However sometimes I do have an abstract base class with abstract methods. For example "Init", "Execute", "Deinit".

When I make children of it, the realizations of the abstract methods have to be dynamic dispatch.

And the problem comes when I make an abstract method in the base class involving the other abstract methods. For example: "FullCycle" containing:"Init" -> "Execute" -> "Deinit"

Calling any implementation of "FullCycle" from any child object would execute on the right hierarchy level, but if I call it with "Call Parent Method" it mixes the hierarchy levels.

How does it mix the hierarchy levels? Let's walk through this in detail.

You have Parent:FullCycle. Now you are writing Child:FullCycle to override it.

What are you doing in the implementation of Child:FullCycle? One of two things:

  1. EITHER you are completely replacing the behavior of your parent class, in which case you will not use the Call Parent Node at all. In this case, you will write some Child:FullCycle and at some point in the block diagram, you will call Init, Exec and Deinit yourself. Each of these will call the Child class version (or the grandchild version if that's what happens to be on the wire at run time).
  2. OR you are adding behavior either before or after your parent's behavior, in which case you will use the Call Parent Node. The contract of your parent implementation says "I call Init,  Execute and Deinit". So Child:FullCycle does use the Call Parent Node, it should not be calling any of these three functions in is own block diagram -- it's calling parent to do that. You may include new code to run before Init and and new code to run after Deinit. When Child calls up to the parent, it knows that its own implementation of Init, Exec and Deinit will be called (or the grandchild version if that's what happens to be on the wire at run time).

In both cases, the same Init and Deinit are invoked. No mixing levels.

So, the recommendation I think you should learn is:

Do not make any method dynamic dispatch unless you intend the child class to be able to modify that function with its own behavior. That modification can include complete replacement (when the child class does not use the Call Parent Node) or prefixing or postfixing (when the child does use the Call Parent Node and adds additional code before or after).

Message 7 of 11
(4,995 Views)

Thanks for the detailed answer, and also for really understanding what I described.

So it seems like my preconception about the "Call Parent Method" was wrong.

Also I can't use dynamic dispatch on methods of a class just for avoiding conflicts when I happen to copy the class and make it a child of the original one.

I still feel that my imaginary version of the "Call Parent Method", let's call it "Call Parent Method Consistently" would be useful, at least for me. But if it never existed in any programming languages, than accept to miss it .

0 Kudos
Message 8 of 11
(4,995 Views)

> "Call Parent Method Consistently"

That new node cannot exist in the same language as the existing "Call Parent Method" because they both call the same VI. It definitely would NOT be consistent -- it would be like a subVI running one block of code if called through a subVI node and completely different code if called through a Call By Reference node.

If you want that behavior, your parent class should only be calling static dispatch subVIs. There's no class that I know of that could maintain its class invariants (i.e. the documented promises it makes to its user about its behavior) if parents did not re-dispatch. C++ has a rarely-used syntax introduced in its first version that allows the parent method to explicitly call its version always (i.e. regardless of whether it was called from outside or called from a child override), but that syntax lead to terrible bugs and it is now pretty much verboten to use it even though it is still in the language spec. C++ also has something called "shadowing" that allows a child to have a static dispatch method of the same name as its parent class with exactly the same parameter list (aka connector pane). In that mode, the parent would not dispatch because the function call is not dynamic dispatch. That supposed feature allows for the template copying that you're lamenting --- it is also one of the larger sources of bugs in code when you change a connector pane of one version and forget about the other one -- which causes the parent version to come out of the shadow and now it gets called by the child in all cases. The compiler won't catch your mistake and will happily make it all work out, except that your runtime logic is now completely hosed. I very consciously left that out of LabVIEW. C# and JAVA both introduced keywords to handle explicit shadowing, both of which introduce name mangling into their calling semantics.

That behavior you seek may look like a sparkling diamond shining on a hill that you long to own. In my experience, the shining turns out to be radioactive energy that burns you.

Message 9 of 11
(4,995 Views)

Wow, thanks for the detailed answer again!

I learned something today. I think this discussion would serve as a good description about what "Call Parent Method" is and is not. (Even the help of the "Call Parent Method" could be updated a bit, to save people like me from some headache )

0 Kudos
Message 10 of 11
(4,995 Views)