Non-positional object construction (as well as positional) shorthand
Abstract
Non-positional match object fields and identifies in object constructor in general and positional with {.positional.} pragma
Motivation
Same as https://github.com/nim-lang/RFCs/issues/517 https://github.com/nim-lang/RFCs/issues/418
Description
Main idea is match object fields and it's names (i.e it not positional) Rules:
ident --> ident: ident
sth.ident --> ident: sth.ident
Obj(sth.a, b, c: 0) --> Obj(a: sth.a, b: b, c: 0)
positional pragma
positional pragma is pragma that can set common (proc like) object calling, it useful for example when different fields have the same meaning (vectors, matrices, etc.)
type
Vec {.positional.} = object
x: int
y: int
z: int
let vec = Vec(x, vec.y, 42)
Code Examples
type
Vec2 = object
x: int
y: int
Vec1 = object
Y: int
let x = 42
let vec = Vec1(y: 42)
# New syntax
let _ = Vec2(x, vec.y)
let _ = Vec2(vec.y, x) # also work
type
Vec3 = object
x: int
y: int
z: int
let _ = Vec2(vec.y, z: 15, x)
# positional pragma
type
PosVec {.positional.} = object
x: int
y: int
z: int
let _ = PosVec(x, vec.y, 42)
Backwards Compatibility
No response
You can't have it both ways so that the meaning of Vec(x, z, y) depends on whether Vec was declared positional or not. It's a bad idea. We only should have positional as the non-positional way plays havoc with my understanding of Nim's scope rules so I would veto it.
Furthermore the non-positional thing is trivally doable with a macro anyway and then it's explicit: inferFields Vec(x, y, z) can turn it to Vec(x: x, y: y, z: z).
You can't have it both ways so that the meaning of
Vec(x, z, y)depends on whetherVecwas declaredpositionalor not. It's a bad idea. We only should havepositionalas the non-positional way plays havoc with my understanding of Nim's scope rules so I would veto it.
What's wrong with Nim's scope rules ?
I'm fine without positional, so many pitfalls on the caller side - name-matching shortcuts however, quite nice :+1::+1: the scope of the name matching lookup is the object being constructed, of course.
This is something that could also be expanded to function calls - ie for proc f(a, b, c: int), if I call it with f(a, c, b) the language could issue a warning which would be disabled by f(a, b=c, c=b), ie if the name doesn't match, use explicit parameter names.
A macro for this defeats the purpose - the aim is to avoid extra ceremony when it's obvious what is meant (ie when name matches). This is not the case for positional arguments which are never explicitly numbered so by looking at the code, one cannot tell if the ordering is deliberate or not. Naming is always deliberate.
You can't have it both ways so that the meaning of
Vec(x, z, y)depends on whetherVecwas declaredpositionalor not.
It's problem ? Vec must be positional by it's mearning: ordered couple of values. Why caller side must know that uses positional or not ? This should be done in the object's design, not in the constructor.
You shouldn't declare object whose field initilizing order is important without {.positional.}
I think it should just be in nep1: when you object order of the fields is important i.e.
type Vec = object
x: int
y: int
z: int
and
type Vec = object
y: int
x: int
z: int
must be not same decl, then mark object as {.positional.}
I’m not at all sure that positional is needed somewhere other than pattern matching. For pattern matching {.positional.} is quite useful.