Hi all! I filed an issue on Github a few months ago about the lack of nimsuggest autocompletion for object constructors. I was really surprised that I couldn't find any similar issues or forum posts on this topic. To me it seems like the lack of autocomplete support for this feature would be a big deal and other people would have voiced similar concerns. However, given that several months have passed without any engagement on this issue from the dev team or the community, I'm wondering if I'm just doing something wrong.
I always thought that "Type(field: value)" object construction (as seen in the manual here) was the idiomatic way to initialize objects in Nim. But maybe that's not the case? To clarify things a bit, I have a few questions:
I'd really appreciate any input I can get about this topic. I'm really enjoying working with Nim, and it'd be nice to have this confusion cleared up. :)
However, it's comparatively new and so large amounts of code were written without it.
Huh, what do you mean? Ever since I've started using Nim in 2017 that seems to be the only way to even construct objects (aside from wrapping that in a init/newType proc).
Technically, you can create objects without the object constructor syntax, but it's cumbersome in comparison:
type
Foo = object
f: int
var foo: Foo
foo.f = 7
echo foo
type
FooRef = ref Foo
var fooRef: FooRef
new(fooRef)
fooRef.f = 8
echo fooRef[]
Worse, I don't see how you could define such an object as immutable with let. :-/ I wonder how this was done before without the object construction syntax.
My impression is that the common way is to use object construction syntax, but hide it from most code by wrapping it in an initMyType or newMyType proc.
This wrapping is useful because you may want to do more in the "constructor" later (for example, set some values to defaults depending on other initMyType/newMyType arguments) and don't want to change all your object initializations.
It’s possible to create an immutable object using an init constructor as you said in your next post. For instance:
type
Foo = object
f: int
proc initFoo(x: int): Foo =
result.f = x
let foo = initFoo(7)
echo foo
type
FooRef = ref Foo
proc initFooRef(x: int): FooRef =
new(result)
result.f = x
let fooRef = initFooRef(8)
echo fooRef[]
You could even define a constant for the Foo type by replacing let by const.
@sschwarzer, @lscrd: This kind of construction (essentially depending on default) isn't equivalent to explicit Foo(...) calls. A variant object has to be constructed with a manually, because otherwise it'd fail the variant kind may not be changed at runtime rule.
Yes, but variant objects are special beasts as it is indeed forbidden to change individually the value of the discriminator, except by globally assigning the object.
This was not always the case. I remember some versions of Nim which allowed to change the discriminator value. I don’t know what Nim used to do in this case to invalidate the memory area which became invalid. In languages which doesn’t forbid the change, some code has to be generated to reset the memory area. In Pascal, nothing was done at all, at least, by the compilers I used.
Nim has now this limitation which makes the construction syntax unavoidable. This is not a bad thing, but in some cases I suppose it may cause some more memory copies.
I don't use "naked" object construction syntax much. For any nontrivial object type I write a new or init function that wraps it, and then call that elsewhere.
I come from a C++ / Objective-C / Smalltalk background, where the general rule is that an object's fields should be private. Centralizing initialization gives you more control, e.g. if a field always needs to be initialized with a specific or computed value.
(By "nontrivial", I mean objects that have their own logic and aren't just passive containers.)
Just so you know - it is allowed in newruntime (so including ARC/ORC) because as long as you have a default destructor it is safe.
Thanks for the advice! I'm coming from a primarily C# background so I recognize the importance of information hiding. Good to know how it's done in Nim!
My primary interest for object construction in Nim is for external C APIs that use lots of structs (such as Vulkan and sokol_gfx). I really like having inline initialization syntax rather than having to specify fully-qualified members one at a time. But when dealing with large structs with many members it can get difficult to remember their names. Hence my interest in autocompletion. :)