I feel really stupid but here goes. I'm writing a small note taking like program in various languages. My add method fails to compile do to a type mismatch. Here is the code:
import tables
type
JotzCollection = object
entries: Table[string, string]
titleSize: uint32
JOTZ_FILE_NAME: string
proc newJotzCollection (): JotzCollection =
var collection: JotzCollection
collection.entries = initTable[string, string]()
return collection
proc add(this: JotzCollection, name: string, note: string) =
this.entries[name] = note # ERROR occurs here.
proc dump(this: JotzCollection) =
for name, note in this.entries.pairs:
echo("$name => $note")
var collection = newJotzCollection()
collection.add("Matrix", "Was a great movie")
collection.add("Pulp Fiction", "No idea")
collection.dump()
The error is: app.nim(16, 17) Error: type mismatch: got <Table[system.string, system.string], string, string>
Any help is greatly appreciated. Going to do some more reading up on Nim so hopefully either way I can find an answer. Thanks!
proc add(this: JotzCollection, name: string, note: string)
this must be a var if you want to modify it:
proc add(this: var JotzCollection, name: string, note: string)
or make JotzCollection a reference object which is more like class in other languages.
type
JotzCollection = ref object
entries: Table[string, string]
titleSize: uint32
JOTZ_FILE_NAME: string
Could someone provide a simple or complex example for object vs ref object and their recommended usecases to us noobs? I have to admit I've been using the ref object exactly for convenience.
Reading the learning tutorials and manual didn't help me fully understand the difference in usecasewise (am I lost cause?;)). Because of this I didn't get the impression that using ref object as a convenience is frowned upon.
Choosing ref vs object is about the semantics of your type and also it's copying.
If your type represents an unique resource or instance for example a file handle, a database or socket connection or GPU memory, a tree node, a person, a game level. This is particularly true if the resource resource that cannot be copied but should be referred to.
Otherwise if your object is fungible (to use a finance term that mean any dollar bill is the same, any bag of 1kg of rice is the same) i.e. the only characteristic of the object is the value, use plain objects.
For example, you have a distance A and distance B, to compare if they are equal, you only compare their value. Now you have Person A and Person B, they have a position, to compare if 2 persons are equal, you don't just compare their distance to an origin, 2 persons can be different while being at the same position. In short, Distance would be a value while Person would be a reference.
Mutability is orthogonal from value vs reference and the fact that references do not require var to update their field is a limitation of the language (also called deep immutability, vs the current shallow immutability). (It would require write-tracking analysis or a borrow checker).
The second reason to choose ref over plain object is when you want to implement an inheritance hierarchy and want to store inherited subtypes in the same container.
I'd argue that in many cases object variants are superior unless you want this to be extensible by an user (so that that user can easily add their own derived classes).
Note that the last part (adding new variant to an object by an external user) can be solved in Nim via macros. In fact I wrote my own class emulation for variants for my RayTracer: https://github.com/mratsim/trace-of-radiance/blob/99f7d85d/trace_of_radiance/support/emulate_classes_with_ADTs.nim#L264-L268. The idea is that user use registerSubType and registerRoutine to add new subtypes and new functions and the library calls generateClass and generateRoutines at a choice place.
Regarding the disadvantages of inheritance vs object-variants.