this is my current code it runs but im wondering if it would scale badly or have weird behaviors.
MalType* = object
case mk*: Mk
of mklist: lis*: seq[MalType]
of mkint: intval: int etc
compared to this
MalType* = ref object
case mk*: Mk
of mklist: lis*: seq[MalType]
of mkint: intval: int
or this
MalType* = object
case mk*: Mk
of mklist: lis*: seq[ref MalType]
of mkint: intval: int
It really depends. Its very hard to guess the slow parts of your code I recommend running and setting up benchmarks really early. See: https://github.com/treeform/benchy
The ref object are pretty good at most things. If you come form languages like Java, Python, JS, Ruby... they feel like what you are used too. They are easy to pass around. Always mutable. They are just easy to use and I use it as the default object type.
The plain object can be more efficient as they are usually not allocated on the heap and might require less pointer indirection, be better packed in arrays. But they can be worse by doing copies where you don't expect. Require "var" for speed. The bigger and more complex the plain object gets the worse this problem becomes.
But always test!
Examples:
Something like this:
type MyType = object
field: MyType
Even if you have an object variant that has this field in one of its branches, its still direct recursion which makes the object take infinite amount of space, so obviously it won't compile :)
` Require "var" for speed`
Parameter passing and iteration of plain objects should have been fixed
In your case we lack information on how you use the object. Will it be copied? Will it be shared? If you copy it, do you want the copies to be independent or updates on one copy to update the other? Is there multithreading involved?
Usually if shared or shared update: use ref object, if independent use plain object. If multithreading, use plain object or ptr object and manual memory management (oor implement atomic refcounting).
Then once your program works, profile and reconsider choices. Because maybe the copies are expensive but the solution is not to use ref object but to introduce a cache instead.
I would guess that the reason of the slow down is that your code does unneeded copies. See the example, compiled with --mm:arc|orc:
type
Mk = enum
mklist, mkint
MalType* = object
case mk*: Mk
of mklist: lis*: seq[MalType]
of mkint: intval: int
#proc `=copy`(a: var MalType; b: MalType) {.error.}
proc main =
var a = MalType(mk: mklist, lis: @[MalType(mk: mklist, lis: @[MalType(mk: mkint, intval: 1), MalType(mk: mkint, intval: 2)])])
var b = a # use var to force a =copy
b.lis[0].lis.del(1)
echo a.lis[0].lis.len # use a to prevent =sink
main()
The line var b = a makes a full copy of the tree and after modifying one of them, they are different. Using let creates a cursor that doesn't require a copy, as you can inspect with --expandArc:main and it should be faster. The commented =copy error line lets you find the location of copies and try to eliminate them.
In the case of any ref, you should be aware that assignments create aliases and copying a ref object is very fast as it just a pointer and incrementing a shared counter.