RFCs icon indicating copy to clipboard operation
RFCs copied to clipboard

Non-positional object construction (as well as positional) shorthand

Open ASVIEST opened this issue 2 years ago • 5 comments

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

ASVIEST avatar Feb 02 '24 20:02 ASVIEST

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.

Araq avatar Feb 03 '24 08:02 Araq

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).

Araq avatar Feb 03 '24 08:02 Araq

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.

What's wrong with Nim's scope rules ?

ASVIEST avatar Feb 03 '24 18:02 ASVIEST

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.

arnetheduck avatar Feb 03 '24 18:02 arnetheduck

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 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.

ASVIEST avatar Feb 03 '24 20:02 ASVIEST