I recently tried to write up a simple explanation to answer this question to beginners, because I was so often confused myself since I started getting more into VL and also got this question a few times and was not able to answer. I thought of presenting this topic as some kind of a decision tree. Would love to hear some thoughts on this, as this post is also some kind of a preparation for the missing parts about OOP in The Patchers Guide.
You pick a Process, when you:
- want to refactor your patches into modules to get a better overview
- want to abstract functionality into modules that are reusable and can be placed multiple times
- want to split your application into several documents, for example when working as a team
- when you simply don’t care what the data type underneath is, because the Process will decide by itself
You switch to a either a class or a record, when you:
- want to get access to all methods of the object (Create, Update and custom operations) as single nodes, for example to bundle properties on Create
- want to create and destroy instances of objects during the runtime of the application
- want to serialize the state of the node, for example when you want to save and store the properties of an object
Until this point a beginner should not need to care about the innards of a data type, and if he or she wants to go deeper, they will be confronted with the topic of immutability and mutability. So…
You pick an immutable Record, when you:
- want to detect changes of the data inside the record (which a class is not able to do)
- want to implement undo/redo funtionality, because immutable types are able to create snapshots when they change (whereas an instance of a class will change itself and not create a copy)
You pick a mutable Class, when you:
- want to change the data in the data type from multiple locations in your patch, because you can access the methods of the type from anywhere in the application (while a record has to be stored back into a pad)
- want to optimize the performance as mutable data is faster (but for the downside of not being able to traverse back through snapshots).
I know there are hybrid approaches between those two, but for a start I have the feeling these are the most important things to know. For the real deep-dive into the topic, there is absolutely no better resource than this brilliant video by @tobyk:
I think you got the right approach for beginners. Best to just go with process and when you run into the problems that demand a more complicated approach then start looking into what class and record can do.
If me as a learning programmer reads about classes and mutability and I still don’t yet get why that would be useful, but I’ve got other ways to solve the problems in front of me then no problem.
With OOP generally I would frame it conceptually as abstractions that are literally only useful by how much they help an application make sense to the programmer.
For example drawing a rectangle involves some data (position, size, color) and some instructions (start in the top left, draw first row with color until size X reached, then start next row etc).
OOP allows you to create this abstraction where the rectangle is an object that ‘owns’ its instructions and possibly its data.
Instead of a process called DrawRectangle you have an object called Rectangle with an operation Draw (Rectangle.Draw).
Ultimately they both execute the same instructions.
So why do that? The CPU doesn’t care how it gets those instructions. The end user sure doesn’t care.
The reason is basically that in a complicated program it sometimes becomes easier for the programmer to think of rectangles, conceptually, as objects. Objects that have their own data (size position color) and their own instructions (Draw, expand, combine etc).
In an ideal world if a programmer understands the program fine without OOP then it’s not needed.
Literally you don’t need OOP until the moment you do because the program gets too complex to reason about any other way.
Then once you ‘get it’ you start writing simple programs with OOP as well.
The problem is we force programmers into OOP sooner then they are ready when they have to work with an external library or system resource that forces you to work that way.
For me in the early days (not so long ago) I was always confused why do you have to ‘create’ a connection instance, isn’t a connection just some instructions? It’s not intuitive until you’ve tried to make a larger application and the objects concept becomes needed to help understand you own app.
I think an exercise for such a vvvv OOP class could be a breakdown of Stride entity system. As it’s kind of wrapped for convenience in processes, but confusingly is actually its own set of objects.
And then you can add components to the entities, which are also objects.
Thank you for your thoughts! You are totally right, it is important to not confront learners too soon with these principles, unless they come up with something, that requires thinking in objects.
I think for anyone not wanting to dive deeper into creating complex applications (or not needing it, because you want to do something creative and not slip down the rabbit hole), you could easily draw a line underneath “when you want to create and destroy instances of objects during the runtime of the application”. This is a feature that people should definitely get, because it is one of the things that make VL outstanding.
On the contrary, it is also the turning point when people get to see all the other options that they could know, might think they should know them and then possibly be overwhelmed. Maybe the UI could support this, by letting the user enable certain functionalities of the editor in the settings, for example:
- Enable Process nodes for custom objects
- Enable Interfaces
- Enable Forwards