"This" pin

Starting with 2019.2 it is possible to do recursion - perform methods on itself.

Therefore a “This” keyword, I guess in the form of a special pin such as “Index”, now makes sense.

It’s actually possible to emulate the behavior of this by creating the pad of the same type as the containing Class and then set it right after creation, it’s just fairly ugly.

image

This would allow combining functionality of multiple methods inside a single method without having to switch to an Operation syntax and also passing a reference to itself to other objects which might need to read out data or do other things.

3 Likes

We have this on our radar, discussed it in detail already and there is also a longer proposal in our issue tracker. But there are some details to decide on still. Without promoting any solution these are all the questions that we need to tackle:

Since you now can call an instance operation of your own instance the question arises how this feature can be combined with reading and writing properties (via pads). E.g. can a user write a property before calling an instance operation that then reads the already changed property? Can the user do the opposite (write the property after calling an instance method on this)? Can a user write a property and read it back in the calling operation? How would that look like?
Is “This” a node, a pad or something entirely new? If you hover it do you see the instance in the tooltip? How can the user make sure when the current instance gets accessed? Can the user write into a pad before/after accessing This?
Do we want This to work in a record or process as well?

After trying to use my fake this for a bit - using it to call an operation that writes to a few pads and then for like 20 minutes not being able to figure out why it isn’t working, only to find out that the pads are being overwritten in the update operation because there’s no way to specify the order - I can definitely see how this issue - given some of VL’s fundamental features and design decisions - is actually not trivial at all.

From all these replies it seems like your issue tracker is pretty packed, would be definitely interesting to read it one day.

I think This makes sense either as an input pin or a read-only pad or Nodes for each operations of the Class you’re in.

It might make sense to analyse which pads an operation writes to and then use this to ensure that pads are written to only once - in other words if OpA writes to pad a, OpB to pad b and OpC to pads a and b then you could use OpB in OpA pr OpB in OpA but you would not be able to use OpC in neither nor ApA or OpB in OpC. Which however kind of limits the use of this feature and still doesn’t solve the read/write order.

Another approach might be to decompose the operation so the used pads are exposed as special IO pins - input for reads and output for writes. The relation with standard pads would then be more explicit and modification order clear to see. Maybe there could be some shortcuts/sugar that make it easy to connect these pins to their respective pad counterparts or maybe even some way of bundling them. Not connecting these pins would be considered an error.

This approach would work with the special Nodes for calling own operations which I guess would not have a State in/out as that would actually be replaced with the pads.

It’s very much possible that thinking these through will reveal fatal flaws but maybe there’s something useful in there. I’ll try to ponder about it some more, see if I can think of something better.

Pins
Modeling This as an input pin is a pretty nice idea! We thought of everything else, but now that you mention it I’d say that’s the most promising candidate.

On the application side, each instance operation comes with an Input pin to feed the instance. But not only that: Also in the node browser you see that pin. So it is there on the definition. It is there implicitly. The idea to be able to access this pin on the definition side, within some instance operation sounds pretty straight forward and quite nice. There is also no question arising when that This construct will execute or alike since it is an input pin, which just hands over what got fed to the pin from outside before anything else in the patch body will execute.

In the case of a class, we are always just talking about one mutating instance. But when thinking about records this Input pin would always hand over the unmodified incoming instance, no matter how hard you try to modify your state via writing pads or calling some instance operations on yourself. An input pin reflects this behavior best.

And having that input pin named “Input” (like seen on application side) instead of “This” would even be more precise: In case of a record we avoid any confusion. The “Input” is the incoming state, NOT the outgoing state or the state that is already “half modified” (like: one pad got written already) and will eventually be the outgoing state…

And suddenly also and output pin “Output” makes sense… You typically don’t it, but in some corner cases - especially when calling own instance operations within records - you might want to hand over a new state explicitly: the state output that you got from calling your own instance operation.

Something we need to fix
I attached a patch that makes use of your super nice hack. It is perfect for prototyping that feature.
In the first doc this.vl (18.6 KB) we have SelfRefering object that on update calls Up on itself, the Down and then checks if we are back in the same state as before. That works and everybody is happy. But we didn’t mix pads and calls on our instance.

In the second case this2.vl (29.1 KB) I do mix them and came up with one corner case where the patch suggests that a pad should have been written before splitting its state, but it didn’t.
Writing fields currently is the last thing the machine does before leaving the patch.
One way to fix this is to suddenly disallow pads that are connected upstream and downstream on one operation, which would be like saying: per operation, we only have reading the incoming state and writing the outgoing state, but nothing can be done after that. The language would then adjust to what the target code currently does. It might even be cleaner.

Or, currently still preferred: Keep the language as is, but change the target code and write in the right moment.

Let’s call this one the ThroughPassingPadCaughtWhenNotWritingItsField bug, unrelated to the This feature request as we can already come up with weird patches showing the bug without having the feature.

Still, I’d say this needs to have at least the same priority as we shouldn’t change either the language or the target code after leaving the preview phase.

Soo. After fixing the issue you then would be able to mix writing pads and calling operations on yourself. Is that a readable patch? Maybe, maybe not. Maybe even when this got fixed, you should go for other patterns to get a clean readable patch.

In the search for patterns
I’d like to argue that there are two major ways to touch state data:

  • on application side you create some state of an object, you pass that state to the next call. So you always touch that whole object. It’s the bird eyes view onto state.
  • inside a stateful patch, you typically don’t want to think about the whole thing, but about parts of it, namely its fields. No need to for splitting the incoming state and joining another for the outgoing state: you make use of pads that allow to directly interact with parts of the state and the language splits the incoming state for you and joins a new state object for you if necessary.

I think these inside and outside views onto state are fundamentally different ways to touch it. And even if we wouldn’t have to bug up there, I think they just can’t really mix well or you shouldn’t try it. Can I get a hand onto the state that got created via writing into this pad. No you can’t. And can’t just take that state, feed it into another operation call of your own type and then read the modified state via a pad…

So I’d say: If you need the outside perspective onto state within some operation. Then try to stick with that way of expression within that operation. If you just want to write or read a field: Introduce a Setter or Getter Operation that does that for you. You now can order all the calls nicely and always stick to the birds-eye perspective.

With your “nice” little hack it should be possible to try this pattern idea already and give some feedback if this is feasible or not. That would be great! Thanks for your help!

3 Likes

We went for a This node. Here is why: https://github.com/vvvv/VL-Language/issues/21
It’s available for testing in the 2020.2 preview builds https://teamcity.vvvv.org/buildConfiguration/vvvv_gamma_Build?branch=preview%2Fgamma-2020.2&buildTypeTab=overview (login as guest)

2 Likes

Very cool, looking forward to trying it out! 👍👍👍

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.