Hello,
I am trying to understand when to use which of the types I mentioned in the title.
Is this summary crudely correct?
I would like to understand what that "really need to" condition would be to use plain objects in that last point.
imho, having value-type (object) is really easy to understand and treat as it just like any others variables.
I prefer object instead of ref object because of simplicity. I only use ref object when it's just necessary to use it.
You can pass-by-reference the object using var argument for functions but I rarely do so.
Nim compiler is smart enough to decide when to pass by reference or pass by value (except when you use bycopy pragma) even though there is no var modifer.
Nim compiler will calculate the object/tuple size, and if it exceed certain limit, it will be pass by reference, otherwise it will be pass by value. var modifier of course will force it pass by reference, but according to the manual, var modifier usage is if you want your parameter mutable. no var modifier means you cannot modify the parameter value/immutable.
ref object will cause a little confusion, because with or without var modifier you can modify the object value. but the meaning of the var modifier is still the same, with var modifier, you can alter the reference value -> the variable point to another object. while without var modifier, you cannot point to another object, but you can modify the object pointed by the reference.
Nim compiler will calculate the object/tuple size, and if it exceed certain limit, it will be pass by reference, otherwise it will be pass by value.
If that's true, it seems like there's little need to explicity designate a type as a ref object.
Is this the case?
No. While what @jangko said is true, there are cases where you need to explicitly allocate objects on the heap (i.e.: use ref object instead of a stack-allocated object)
One such case is if your object is so big that the stack can't hold it: in that case, the C compiler will throw an error (at least, GCC does it, if I remember correctly). In that case, it's necessary to use a ref type, since they're always allocated on the heap.
You can pass-by-reference the object using var argument for functions but I rarely do so.
there are situation where we cannot avoid ref, as @amalek and @Stefan_Salewski already explained.
FYI, I am gathering these notes here: https://scripter.co/notes/nim/.
The notes are an exercise to help me learn Nim, but to all collect all my QnA in one place, and also share with someone like me who might need them.
If someone has time, feel free to suggest corrections in those.
The main conceptual difference between smth and ref smth is sharing. You can have several locations pointing to ref smth and if you change smth from one location the change will be visible through another location. That's not the case with non-ref smth. So ref object is not the default, you have to consciously chose it depending on your design. Also inheritable hierarchies are mostly useful when they are ref object.
Size of the objects (and whether it fits on stack) almost never has to be considered in reality.
Under the hood ref smth implies heap allocation and an extra access indirection. Heap allocation is more expensive than stack allocation, so it might matter in performance critical code paths. But then again, refs are generally faster to assign to each other, as they take up a single machine word.
Tuples are almost like objects. Tuple fields are always public. Tuples can be anonymous, and they can't be inherited. Generally they are used to represent some short living values that don't deserve their own named type.
there are situation where we cannot avoid ref, as @amalek and @Stefan_Salewski already explained.
I know, when there's a need to use var function parameter, it's a good sign that I need ref object instead of object. It's as @yglukhov mentioned, we choose that because of design.
For problem exploration, it's better to simplify things first so I choose object first