Hello everyone,
in the example below the initCar proc creates a new car and defines sensible default values. Now I decide to extend the car with a new attribute maxSpeed: Natural. By default the compiler will set the value of maxSpeed to 0 which doesn't make sense for cars. As module author I'm in charge to set a default for maxSpeed in the constructor initCar. With a lot of attributes one can loose track of them and I may forget to init the car with a maxSpeed of 100 . Hence my question:
Does Nim provide a pragma to ensure that all attributes of an object are provided during its creation using the Car()-syntax (here in initCar)?
type
Car = object
numOfWheels: Natural
numOfDoors: Natural
color: string
hadAccident: bool
distanceDriven: Natural
# and many more attributes ...
# create car with default values provided by module author
proc initCar(): Car =
Car(
numOfWheels: 4,
numOfDoors: 4,
color: "red",
hadAccident: false,
distanceDriven: 0
)
var c = initCar()
# user wants a blue car
c.color = "blue"
Hello, I believe the {.requiresInit.} pragma may be what you are looking for. it will throw an error if the object is initialized with a missing value.
type
Car = object {.requiresInit.}
numOfWheels: Natural
numOfDoors: Natural
color: string
hadAccident: bool
distanceDriven: Natural
# and many more attributes ...
# create car with default values provided by module author
proc initCar(): Car =
Car(
numOfWheels: 4,
numOfDoors: 4,
color: "red",
hadAccident: false,
#distanceDriven: 0
# don't forget to set maxSpeed to 100
)
var c = initCar()
# user wants a blue car
c.color = "blue"
in order to get rid of the deprecation warning place the pragma after the object name
Car {.requiresInit.} = object
and that should resolve it.
as for the documentation I found that pragma in the nim manual https://nim-lang.org/docs/manual.html so reading through that you will find a wide variety of pragmas. I don't if all of them are specifically documented in a list somewhere so just going through the manual may be your best bet
Is there an alternative (and future-proof) way to add a pragma to an object?
Nim's error messages can be confusing and vague, but this one is quite helpful:
type pragmas follow the type name
This subject has been discussed many times: a language enforced, but optional, yet inheritable initialization method.
Having thought about it for about a year, I wonder if this could be done with a "backtick operator" similar to how op codes and access methods like + and [] are done.
Something like:
type
Car = object
model: string
maxSpeed: int
proc `()`[Car](maxSpeed: int = 55): Car =
result.maxSpeed = maxSpeed
var c = Car()
assert c.maxSpeed == 55
My example does not deal with "kind"ed objects, so it is not complete.
That's a good syntax, but it's already taken for construction and it would conflate construction with initialization, default values and inheritance behavior. For the first two, we now have Car(..) and initCar(..). For default values, I would be happy with allowing this:
type
Car = object
model: string = "unknown"
maxSpeed: int = 55
And if we introduce some kind of inheritance driven initialization behavior ( explicit super calls or automatically invoked super-type initializers, e.g.?), we turn Nim into an object oriented language, which would not be good IMHO.
OO can be (and has been AFAIK) implemented by meta-programming. Let's keep the core language a lean, orthogonal set of building blocks for higher level stuff.
Why is this pragma not the default behaviour? It seems to me that's a better practice to ensure that all fields are initialized by the programmer than relying on the compiler initializing them with a default value.
There are basically two different schools of design:
Nim is slowly moving from (1) to (2), see also https://github.com/nim-lang/RFCs/issues/49 (which was actually written before our RFC process...)
@gemath, I like the idea of default values; and perhaps that is enough.
Then the times module will stop giving me "0 (invalid data!)" on "echo DateTime().month". I know that sounds like trivia, but it adds another layer of complication to object deserialization.
I strongly suspect such a change would require that the default values be fully resolvable at compile-time.
I'll be watching RFC 49 with interest.