-
Notifications
You must be signed in to change notification settings - Fork 23
compiler support for object construction shorthand (full fields initializer) #517
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is inconsistent with named parameters in function calls where no such restriction exists. |
While we're at it, would it be beneficial to introduce some consistency in named parameters syntax, that is using |
Sorry to diminish the effort that has gone into implementing this but do we really need this? What problem does this (or #418) exactly solve? Is it worth the future maintenance and extra code in the compiler and required documentation? I really cannot empathize with wanting this which is weird because the "stop adding new features" crowd seems to be missing here |
It is easy to add support for ommittance. var x = Vector(1, 2)
var y = Vector(1, z: 3)
var y = Vector(1, y: 2) If needed, I can somhow add support for arbitrary orders. vector(1, c: 3, b: 2)
vector(c: 3, 2, a: 1) |
Colons are already overused in Nim though. Object constructors could use |
Adding new special syntaxes to nim could increase its complexity. I can remember how confusing it was to grasp the dissimilarity between the for loop tuples syntax with |
This comment was marked as off-topic.
This comment was marked as off-topic.
Here's what I meant: https://play.nim-lang.org/#ix=4sog The proposed object construction syntax has similar gotchas as documented at the top. |
I'd like to pose a few question which this RFC doesn't yet answer to the full extent. What problem does it solve? (Benefit)The necessity to repeat object field names when typing object initialization by hand. Does the solution introduce any new ways for bugs to occur? (Cost)Whatever can't be statically analyzed by the compiler will be the source of bugs. To the same extent as with functions, declarations and usage usually have some distance between them, so relying on order without proper tooling support (such as definition tooltips) is unreliable. With tooling support (autocompletion) the issue simply disappears. Additionally, it's a new special rule with an exception (special-casing the single field). This conflict with converters already hints at possible ambiguities. Do we want it solved? Why?The main argument for accepting this RFC is:
Allow me a little digression, as it's hard to find proposals equal to this RFC, so let's look at the next closest thing it's inspired by.
So, to sum up:
Does the new syntax make the language more or less consistent with itself?Well, it definitely brings objects closer to tuples and functions, whether it's a good or a bad thing. Regarding the tuples, isn't the common advice "when you have more than a couple of fields, consider using an object(|struct|class|whatever) instead of a tuple"? I always supposed the logic behind it is the requirement to be more explicit which objects force you to do. Why does this need to be a part of the language?Not qualified to assess, so just the questions: Why is this better than alternatives?
type
Adj = enum
Mimsy, Galumphing, Slithy
Bandersnatch = object
name: string
age: int
kind: Adj
converter bandersnatch(x: (string, int, Adj)): Bandersnatch =
Bandersnatch(name: x[0], age: x[1], kind: x[2])
# Three ways to init an object positionally with a converter
let snark = ("Snark", 147, Galumphing).Bandersnatch
let jabberwocky = Bandersnatch(("Jabberwocky", 147, Galumphing))
let boojum = bandersnatch ("Boojum", 147, Mimsy) PrecedenceHelp me out with this one. C, C++. What else? More languages kind of have this via function call, such as Python's PS: a couple of funny fitting quotes from one of the docs below:
__ |
I honestly don't understand why you are so vehemently against it. Here are a couple of usages inside the stdlib and the Nim compiler that would become more readable afterwards. Now: uint64x2(hi: hi, lo: lo)
HSlice[T, U](a: a, b: b)
NimStringV2(len: len, p: p)
(ref AssertionDefect)(msg: msg, parent: nil)
StackTraceEntry(procname: it.procname, line: it.line, filename: it.filename)
StackTraceEntry(procname: procname, filename: filename, line: line)
LineInfo(filename: n.getFile, line: n.getLine, column: n.getColumn)
Timezone(name: name, zonedTimeFromTimeImpl: zonedTimeFromTimeImpl,
zonedTimeFromAdjTimeImpl: zonedTimeFromAdjTimeImpl)
TimeInterval(nanoseconds: -ti.nanoseconds, microseconds: -ti.microseconds,
milliseconds: -ti.milliseconds, seconds: -ti.seconds,
minutes: -ti.minutes, hours: -ti.hours, days: -ti.days,
weeks: -ti.weeks, months: -ti.months, years: -ti.years)
IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
ItemId(module: x.module, item: x.symId) After RFC: uint64x2(hi, lo)
HSlice[T, U](a, b)
NimStringV2(len, p)
(ref AssertionDefect)(msg, parent: nil)
StackTraceEntry(it.procname, it.line, it.filename)
StackTraceEntry(procname, filename, line)
LineInfo(n.getFile, n.getLine, n.getColumn)
Timezone(name, zonedTimeFromTimeImpl, zonedTimeFromAdjTimeImpl)
TimeInterval(-ti.nanoseconds, -ti.microseconds,
-ti.milliseconds, -ti.seconds,
-ti.minutes, -ti.hours, -ti.days,
-ti.weeks, -ti.months, -ti.years)
IdGenerator(m.itemId.module, m.itemId.item, typeId: 0)
ItemId(x.module, x.symId) |
I agree that it's harmless but maintaining it should not take priority (I understand it might also not need maintenance). I think the strategy for this historically has just been putting it in the experimental manual but off the top of my head I can't name similar nonessential features |
This is perhaps the argument that doesn't quite hold water - ie this syntax hides an essential part of object construction: which field is being assigned to instead asking the reader of code to first look up the object definition then remember the order. "Smarter editors" is kind of a poor argument in general which relies on the rube-goldberg approach of introducing an less informative syntax then changing all editors out there to gain back the information loss. The syntax is only redundant under a very specific condition: when the name of the argument matches the field name exactly - under all other conditions, this proposal removes information that the reader has to recall through other means - A more pointed argument "for" this proposal would be that spelling out the field names suffers from "too much information" - ie people should reasonably learn to remember field orders and if field order in an object changes, well, too bad for all code out there.
This is not a valid argument: after this proposal, changing field order breaks backwards compatibility - as a library author, you don't control how the users of your library interact with it, the language rules do. By permitting this syntax, we will see more applications break overall whenever libraries change field order: this means for example that in the standard library, we must never ever again change field order of any object. The safer option here is to introduce the shorthand syntax for the "same name" case only - this one still allows safe casual reordering of fields in an object, and promotes a style that is good for readers of code in general, namely that of consistently naming the same thing the same way. |
Readability is not about "how much" but "how clear". The After RFC example is obviously not less readable (in this specific case, where the variable names were carefully chosen), but gets into the category of code which I should be cautious about (opposite to just scan through), so there's a considerable chance I'd need to go to the declarations.
As if I never complained about anything Nim has. :)
Other languages in this case are C, C++. People complain about almost all features of the language. Sorry I couldn't find specific examples. What are other languages?
Too bad my attempt to explain it thoroughly in the previous post failed. Was it too long to be convincing? PS: make |
That conflates the object's field scope with the current scope and thus would be a weird special case in the language whereas this RFC makes object construction more similar to routine calls. |
Rust in particular. |
I've already answered that this is not true. They permit only the same-name shorthand as in #418. struct Foo {a: u8, b: u32, c: bool}
fn main() {
let a = 42u8;
let b = 90210u32;
let c = true;
let foo = Foo {a: 0, b: 1, c: false};
let bar = Foo {a, b, c};
// let xyz = Foo {a, b, false}; // Error!
// let baz = Foo {0, 1, false}; // Error!
} You can check the commented lines really produce errors in the playground. |
Yes, but it's "similar". Would you mind Rust's solution too then? Because I would, see above. |
In Rust you have to declare the struct as a tuple struct which names the fields Java records/Kotlin data classes/Scala case classes allow naming these fields and allow positional constructors however the declaration syntax is still different like Again, we can require |
I probably just wouldn't care. If you think #418 it's problematic, not having it is fine by me. This RFC is negative value, on the other hand.
Tuple structs are just Nim's tuples. |
Why? Sounds like a reasonable compromise. |
I was going to say it just looks like a macro pragma that defines a constructor proc but there are some things that aren't possible with that like constructing different object variant branches Also it forces tons of types to be declared as type
Foo = object
x, y: int
Bar {.positional.} = Foo
echo Bar(1, 2) |
seems pretty reasonable for what several people have reacted to as a "dangerous" feature in general and "useful" in special cases only - ie this way, the author of the type assumes responsibility for not changing the field order and tells "consumers" of the type about it: I don't foresee it being applicable to "tons" of types outside of those that already have a it also doesn't prevent "future" acceptance of the more specific name-matching rule, which, although it's a bit different than usual in Nim, still could be explored separately from this RFC and alleviate some of the more "obvious" redundancy. |
This has stalled for a while now, I now agree requiring Should be fine for 2.2, though there are a couple more steps we could take, like allowing tuple types instead of just objects in these constructors and in normal object constructors for named tuples. |
Uh oh!
There was an error while loading. Please reload this page.
Motivation
The Nim language supports named field names with values to construct an object. It works perfectly fine and is unambiguous, since it's different from function calls or type conversions syntactically.
However, compared to function calls which support both named and positional parameters, this way of construction seems not to be succinct and looks redundant. Indeed, it is. The field names are not actually needed, which means we can construct the object using unnamed field values.
var x = Vector(1, 2, 3)
just works. Why not add a shorthand for the object construction and call it a day?Description
This RFC proposes a new way of object construction, which makes it handy to construct objects. You can mix it with named field values.
All the fields need to be initialized in order. It means all cases below should give a proper error message (it might be relaxed in the future).
As to object variants, the discriminator must be known at the compile time because the compiler needs to get hold of the exact selected branch. In the case below, the value of
flag
should be a constant.For historic reasons, an object with a single field cannot be initialized with an unnamed field value. It should always be interpreted as type conversions. The field name needs to be written explicitly in order to construct an object.
Backwards Compatibility
It can start from an experimental feature. It might disturb function calls with the same name as the type from other modules, which means the object construction shorthand might take precedence over function calls.
The text was updated successfully, but these errors were encountered: