vvvv / VL-Language

The official repo for the design of the VL programming language
31 stars 0 forks source link

[Explanation] This node #21

Closed gregsn closed 3 years ago

gregsn commented 4 years ago

There has been a discussion in the vvvv.org forums: https://discourse.vvvv.org/t/this-pin/18127

We were embracing the idea of having This modelled as an input pin called Input or State Input with the idea that later on Output or State Output might get added.

We finally didn't go for that idea but implemented a This process node.

Here is why:

We never need to feed the State Output

Let's start with a Record You define an operation on your record and want to output a new snapshot. Here is what you'd do:

So we felt like there is no need to add some new idea to get a new snapshot outof a record instance operation. No matter whether you write the pads on your own or delegate that task to another operation...

Class When changing fields inside a mutable data type you need to reason about what to do in what order. #19. But one way or the other: You are just mutating yourself. This is reflected on application side: the state Input and Output pins are connected. This visually says: The output is always the same instance as the ingoing one. It would be plain weird if you'd be abe to overrule that within the operation and just write something else into the state output. Add another output pin if you want to create a new instance manually and hand that over via an explicit new pin.

So all in all. We don't need a way to write into the State Output.

What does that mean for the Input

So we had issues with the right name for that input. We could have called it This, but there is no pin on application side with that name...

Decision

We went for a process node This that always outputs the incoming instance. It helps explaining how to translate C# code into a patch and feels lightweight. It is modeled as a process node as it only makes sense in stateful context

azeno commented 3 years ago

Little follow up after looking into the topic of making This available inside the Create operation:

In order to have This available inside of Create the following steps are required: 1) Create the instance using a default constructor. Its fields are not intialized yet. 2) Call the private __Create__ method on that newly created instance. The __Create__ method will map to the user defined Create operation. At the end it will initialize all fields with the values as definend in the patch. 3) Return the instance

These changes are now available in VL >= 2021.4-0127

Note that the fields will be written directly and only after __Create__ are properly initialized. This means that calling any other operation during that call will have undefined behavior because either 1) the fields they're accessing are not initialized and will most likely result in a null pointer 2) or the values written to the fields will get lost since __Create__ will write them again at the very end

So currently we can't use the This node to call another operation like Reset during Create.

Two ideas to solve this issue

  1. Introduce a new built-in operation OnInitialized This new operation would be called by the system right after Create. It would not be visible from the outside to ensure it's only called once.
  2. Modify the execution order of Create
    1. Initialize all user defined fields with defaults first. This ensures that the values written by another operation don't get lost (at least for the mutable case) as well as fields accessed by another operation are initialized.
    2. Ensure that the Create fragments of all process nodes are traversed before the This node. Ignore those which have been traversed already. This ensures that process nodes are initialized before they get accessed by a call to another operation (at least as long as there's no cycle which would lead to a null pointer).
gregsn commented 3 years ago

@azeno and I have discussed this internally in detail and I thought it would make sense to also write down the ideas as this was a rather long discussion.

Ok so here is what we touched:

My intuition here was that introducing Initialize would be more of a pattern forced upon the user and thus not as versatile as just figuring out how Create and This work well together. But then again: if many user patches turn out to actually make use of the pattern explicitly by calling Initialize on Create manually (by connecting an Initialize-call to a This node), then maybe that pattern can still be introduced later on?

Not sure. And not sure about the order how to tackle the issue. Still, let's see which questions would need to be solved first, before being able to decide which one is the better solution:

So let's assume the user calls Initialize on Create on the own instance (This) manually. What are the open questions when defining how This shall operate on Create?

How is the answer to these questions connected to other quests, like the one regarding execution order #15 and the other one regarding through-pads #19? Is it possible and clever to try to get a better understanding of those issues first, before tackling these special cases?


A possible way we could go:

By this, we'd have quite some issues out of the way already. The user could express manually when to write fields, so before or after a recursive call. It feels nice to have a working manual idea, that already can be used if the to be invented magic doesn't work as expected.

The magic sauce could be:

azeno commented 3 years ago

The magic sauce is now implemented in 2021.4. The core ideas of this issue have been tackled.