In general you shouldn't worry about "idiomatic way", Nim doesn't have an "official way" of doing things. Each technique is more suitable for a particular set of problem. Handling the freedom that Nim gives you is not a matter of learning Nim, but a matter of learning programming concepts in general.
Nim is awesome because it allows you to apply programming concepts with as little hassle as possible. If you want strict guidelines about how to do things, then Nim is not a good choice (for example Rust follows this philosophy).
idiomatic way of composing objects and structure the code in Nim.
Specifically for structuring data, I have one advice: you use tuples instead of objects whenever you don't need the extra features that objects provide.
type
MyType1 = tuple
a: int
b: float
MyType2 = tuple
a: MyType1
b: MyType1
Specifically for structuring data, I have one advice: you use tuples instead of objects whenever you don't need the extra features that objects provide.
I'm against this advice. A tuple should be used when you need its feature (ie. tuple unpacking, array-like accessing syntax, etc.). Otherwise, an object should be used, and here's why:
type ObjA = object a: string b: int ObjB = object a: string b: int A = tuple a: string b: int B = tuple a: string b: int proc foo(a: A) = echo "A" proc foo(b: B) = echo "B" # foo(A(("string", 10))) Error: ambiguous call; both in.foo(a: A) [declared in /usercode/in.nim(15, 6)] and in.foo(b: B) [declared in /usercode/in.nim(16, 6)] match for: (A) proc foo(a: ObjA) = echo "ObjA" proc foo(b: ObjB) = echo "ObjB" foo(ObjA(a: "string", b: 10))
Please also note that object does not have any overhead over tuple if you don't use it's more advanced features (ie. RTTI, but that's going away with newruntime).
The only way to construct a tuple is to remember exactly the order of the fields.
You can use named fields instead of the [] operator. This is valid:
let x = (a: 5, b: 4.2)
echo x.a
Using tuple instead of object when proper object is needed creates broken code, by definition.
Because comparing 2 different entities with similar fields/attributes must not be equal (A == B).
Comparing 2 different tuples with similar fields/attributes is equal.
Comparing 2 different objects with similar fields/attributes is not equal.
I think Nim favors structured programming which is just objects/structs and free-standing procs that work on them.
Basically there is no "methods" only attached to an object type like in OOP.
So you would organize your code like C code, which is the most popular language with structure programming bias, except that you have:
Concepts documentation is in experimental: https://nim-lang.org/docs/manual_experimental.html#concepts
In terms of files, in general Nim code is structured with a common type file, and then procs for each type are in a separate file unless everything can be put together in a single file of reasonable length.
The common type file is due to Nim not supporting recursive imports. This causes difficulties when we want a type to have private field, but need to export it from the common type file for it's own basic proc.
But that's solvable in the future and private field is a technical solution to a social problem. In Nim we trust users to understand that "With great power comes great responsibility".