» 50: Generics
This site relies heavily on Javascript. You should enable it if you want the full experience. Learn more.

50: Generics

helo patchers,

here is the fourth in a series of blogposts about our forthcoming nextbigthing that we still call vvvv50 (50). If you haven't already done so please first read the previous issues with the titles:

  1. 50: That-Next-Big-Thing.-An-Overview.
  2. 50: Colors
  3. 50: Properties
All information given here is still preliminary and subject to change.

So "Generics", uff, sounds so serious, I know. Still we have to mention them now because it will help you understand things further down the road. And you'll be surprised how easy the concept is to grasp. Promised.

No generics in 45

In vvvv45 (45) we don't have generic nodes. What we have instead is a lot of identical nodes like eg. "Zip" available for different datatypes. There is a Zip (Value), a Zip (String), a Zip (Color)... and the nodebrowser is full of such duplicates for a lot of nodes, like GetSlice, Select, Queue,... all of which are nodes where the operation they do for us is actually independent of the datatype they handle. We can call such operations "generic" in the sense that if for example you think of the functionality of a Zip it is not really important to know whether it zips slices of a spread of strings or a spread of colors. Easy? Yep.

In a 45 patch nodes like Zip already look generic because precious screen-space is saved by leaving out information that would not help in understanding the functionality of a patch. Still the nodebrowser is full of duplicates you have to explicitly choose between.

Only recently we've introduced a way for plugin developers to easily create duplicates of those generic nodes for their own datatypes but that is really only a workaround to the fact that we don't have support for generics built right into 45. Still better than nothing, see: generic-nodes.

Generics in 50

Now when we say 50 has support for generics we can advertise that in two ways:

For the casual patcher

First, the nodebrowser will have less nodes to choose from because it can leave out many duplicates (well, it will have many more other nodes but at least no datatype-duplicates). If you want a Zip you don't have to decide for which type you want it. Just place it in the patch, connect anything to it and you're done. 50 will figure out what you mean.

In a 50 patch a zip is a zip is a zip and you can connect any datatype to it. So in this example the left zip is forced to operate on values and the right zip is forced to operate on colors only.

For the pro patcher

Secondly (and this is probably a bit more abstract to wrap your head around, but please try:) when you patch an operation you can be unspecific about the datatypes of its inputs and outputs. Sounds exciting? Here is an example: Consider someone patched the functionality of a Queue. This is what it could look like in 45 and 50:

Queue (Value) in 45 vs. Queue (Generic) in 50

Reading those two patches:
The Inlets and Outlets (Item, Insert, ..) of both implementations are the same and the FrameDelay, as we've already learned, is replaced by a property (called "Items" here). And while both kind of look generic, in the 45 implementation we see the Item obviously is a value IOBox. Therefore we know that this is a specific implementation for values.

In the 50 implementation you see all the operations (clear, insert, take) are working on the generic collection type Spread, ie. they have not yet been forced to operate on a specific type like Spread of value or Spread of color. And you can easily identify pins, in- and outputs and the property that are generic as they are visualized in a different way (ie. only showing their contour). So here is a single implementation of a queue that works for any datatype at a time, even ones that you create yourself (more on that in the next post).

What you take away here is that 50 comes with a set of generic spread operations (insert, take, zip,...) for handling any kind of data and the problem you sometimes faced in 45 where individual spread operations were only available for specific datatypes, is no more.


And the best of it all which is really only a side-note here:
For all those basic generic spread operations we don't have to write a single line of code. In 50 we can magically make use of that functionality as it comes with the .net library. Besides the fact that this saves us a lot of time it also means those basic operations can be assumed virtually bug-free, not only because we didn't write them but also because Microsoft has been taking care of testing that code since years.

That just for a little soothing happynew50 update. Now fasten your seatbelts for the next issue with the blockbuster title "Custom Datatypes".

If what you just learned makes you feel like inserting coin, don't hesitate and click: /downloads|vvvv?.

joreg, Thursday, Jan 29th 2015 Digg | Tweet | Delicious 8 comments  
vux 30/01/2015 - 02:39

As much as I enjoy the blog posts, I would really like to see a bit more complicated examples, for now it feels a little bit too much "hello world" style samples.

This one is actually a perfect example so let's expand a little bit.

First this could be achieved to a certain extend with vvvv45 (on the exception of value/string/color/transform types which have a different interface for creation), since there is already a connection handler, so overriding this functionality can allow to have this (more or less store the type in a config pin to set connection).
Adding functionality to draw in patch from plugins (custom iobox and pin editor) and that would be pretty much set.

So now let's try to go out of the hello world case, and go into some real world scenarios.

1/Structs and "binary" compatible types.

Let's say we connect a float to a zip node, now we can't connect a Vector2 to it, whereas it could be totally acceptable (Vector2 is just two floats packed together, and with bit of il magic conversion is basically cost free, no need to copy memory at all).

2/Classes

Now things become more interesting :)

Let's say we have two interfaces iDummy1

and we have 2 classes cTest1 and cTest2 (both implementing iDummy1)

Now we connect something outputting cTest1 to the zip node.

Two scenarios are now possible:

  • We can't connect cTest2 anymore, since expected type is cTest1 (so zip nodes locks inputs/outputs to cTest2)
  • Zip node allows to connect cTest2, because it notices that cTest1 and cTest2 share a common interface, in which case Zip output is no more of cTest1 but iDummy1 (which can cause problem if output is connected to an input expecting cTest1)
  • On first connection (cTest2), we scan for all interfaces/class/subclass that cTest2 implements, and offer a dropdown to select which type we effectively want to bind. That's a bit more cumbersome on patching but offers the best flexibility.

3/Conversion

Let's now say we use Color4 and Vector4 (from SharpDX).

In c# you can just do:

Vector4 v = new Vector4(some data here)
Color4 c = v;

Since there's an implicit operator under the hood.

So would this Zip node (as example) which has a Vector4 input connected also accept a Color4 next to it (otherwise we end up in same situation having converter nodes everywhere)?

gregsn 30/01/2015 - 04:33

hey vux!

thanks for your ideas on this!

vux said
As much as I enjoy the blog posts, I would really like to see a bit more complicated examples, for now it feels a little bit too much "hello world" style samples.

i do understand your impatience. And there may be some more people feeling the same regarding the pace of explanation in these blog posts. And we will need to go into quite more detailed / advanced topics sooner or later. That's for sure.

Anyway we decided that is not a good idea to confront people with too much details. Why? Because we want everybody to be able to follow. These blog posts are intended as a glimpse on the big picture, not the potentially distracting details.

vux said
First this could be achieved to a certain extend with vvvv45 (on the exception of ...

Oh well. Well, the exceptions, right. And not only the fact that there are some types that are treated differently than others. Also the fact that spreads by itself are not treated as a type at all, they don't exist as a data type in vvvv45 since they are there implicitly everywhere as you know, which makes them another exception. You need to use Zip (Value Bin) to zip spreads of values...
So there are some issues that are intrinsic to vvvv45 that don't allow to build in generics into vvvv45 in a pleasing way.

That said there may be some ways to improve the situation. But this should be discussed elsewhere.

vux said
1/Structs and "binary" compatible types.

Let's say we connect a float to a zip node, now we can't connect a Vector2 to it, whereas it could be totally acceptable (Vector2 is just two floats packed together, and with bit of il magic conversion is basically cost free, no need to copy memory at all).

Let's first see what we expect the zip node to do:
A Zip (Value) shall take one value from the first spread, one Value from the second spread ...
A Zip (2d) shall take 2 values from the first spread, then two values from the second.

In vvvv45 these are already different nodes: they generate different output!
So it feels natural that a zip in vvvv50 at least treats those cases differently. I guess we agree on that.

We now can discuss about

  • when a spread of values can be seen - via implicit conversion - as a spread of 2d vectors.
  • when a spread of 2d vectors can be seen as a spread of values.

I guess a spread of (1, 2, 3) makes a bad spread of 2d vectors and implicit conversion should not occur.
The other case that you have a spread of 2d vectors which should convert into a spread of values is of course thinkable. Let me just tell you that we have it on our radar and also already combine generics with some few implicit conversions.

2/Classes
The type inference which is running in the background is taking care of subtype realtionships, yes.

It is maybe of importance to explain that a zip node is not fixed to a certain type once the first connection has been made. After every single change of the patch - like a new connection - the system will take a fresh unbiased look at the patch and figure out the types for the generic nodes.

That way it typically figures out the right type for you, which might be a super type of two classes.
And in the rare cases where it doesn't, you will be able to interfere and make type annotations or force a link that the system wouldn't allow based on the currently infered types. After the forced link it will again look at the patch in a way that the new link is nothing special. It might then infere another type or might also give you a red link somewhere when the forced connection just doesn't make any sense - to the system that is... ;)

3/Conversion
As discussed before it is on our radar.

Thank you again for bearing with us!

sebl 30/01/2015 - 14:24
gregsn said
Anyway we decided that is not a good idea to confront people with too much details. Why? Because we want everybody to be able to follow. These blog posts are intended as a glimpse on the big picture, not the potentially distracting details.

i think our brains can handle more input.


and i really like what i see. can't wait until node...

gregsn 30/01/2015 - 15:27
sebl said
i think our brains can handle more input.

hehe. i know. sorryy.

on the other hand: i just tried to remember the last "hello world" that shows how to implement a generic queue. and failed ;)

but you guys are still right: we are leaving out some details. feel invited to ask for more!

sebl said
and i really like what i see. can't wait until node...

so good to know. we count on you, sebl! :)

fibo 03/02/2015 - 11:36

Really good article, it explains clearly the concept. The feature is nice and will help simplify and create cleaner patches as well as reusable pieces of software. Good job!

fleg 03/02/2015 - 17:40

Well, I guess the point of these posts is to to review the current situation, give a simple example how things are changing and what the benefits are ... without being able to play with it.

So, to me: Nailed it.

TheSeven 03/02/2015 - 22:59

I'm wondering if spread apparently being a proper data type now implies that there can be spreads of spreads (i.e. multi-dimensional spreads without binsizing!) that you can wrap and unwrap layer by layer.

joreg 04/02/2015 - 01:13

@TheSeven: exactly, good thinking. only also understand that the usecase for spreadofspread in 45 is mostly a workaround for lacking custom datatypes...

  • 1

anonymous user login

Shoutbox

~4d ago

~7d ago

joreg: The Winter Season of vvvv workshops is now over but all recordings are still available for purchase: https://thenodeinstitute.org/ws23-vvvv-intermediates/

~14d ago

schlonzo: Love the new drag and drop functionality for links in latest previews!

~22d ago

joreg: Workshop on 29 02: Create Sequencers and Precise Clock Based Tools. Signup here: https://thenodeinstitute.org/courses/ws23-vvvv-08-create-sequencers-and-precise-clock-based-tools-in-vvvv-gamma/

~29d ago

joreg: Workshop on 22 02: Unlocking Shader Artistry: A Journey through ‘The Book of Shaders’ with FUSE. Signup here: https://thenodeinstitute.org/courses/ws23-vvvv-12-book-of-shaders/

~1mth ago

joreg: Talk and Workshop on February 15 & 16 in Frankfurt: https://visualprogramming.net/blog/vvvv-at-node-code-frankfurt/

~1mth ago

woei: @Joanie_AntiVJ: think so, looks doable

~1mth ago

xd_nitro: Anyone remember who increased projector brightness by removing some components that product the color?

~1mth ago

Joanie_AntiVJ: This looks super interesting (vectors over network) would anyone here know how to implement this in beta? https://github.com/madmappersoftware/Ponk