[vim/vim] Language Specification: Default constructor is a foot gun (Discussion #13197)

45 views
Skip to first unread message

dkearns

unread,
Sep 26, 2023, 12:13:53 PM9/26/23
to vim/vim, Subscribed

The default constructor's parameter list order matches that of the field declarations. This seems like a probable cause of surprising bugs for casual users. I don't really think the feature pulls its weight.

Generally languages with this relationship between constructor parameters and fields use the constructor's parameter list to automagically declare fields, the order of which is unimportant. In Vim9 script the relationship is inverted.

E.g., switching the declaration order of the fields below causes Subtract() to produces a different result without error. There's an obviously natural ordering of fields here but this not always the case.

class A
  this._x: number
  this._y: number
  def Sub(): number
    return this._x - this._y
  enddef
endclass

echomsg A.new(1, 2).Sub()

Field declaration order essentially becomes part of the class 'interface'.

The best solution is to support named parameters and given the languages purported interest in call-site annotations this would be generally useful.

The obvious workaround is to declare explicit constructors.

I noted this issue when there was an initial request for comments but still managed to stumble over it.


Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197@github.com>

errael

unread,
Sep 26, 2023, 1:38:52 PM9/26/23
to vim/vim, Subscribed

In todo.txt

  - add to help: when using a default new() method then reordering object
    members may cause trouble.  Can define new() without arguments to avoid.

I've gotten into the habit of putting

def new()
enddef

in all classes.


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7115458@github.com>

dkearns

unread,
Sep 28, 2023, 10:28:14 AM9/28/23
to vim/vim, Subscribed

Can define new() without arguments to avoid.

I don't understand what this achieves other than requiring a second named constructor with parameters.

I've gotten into the habit of putting ... in all classes.

You mean as a discipline to remind you to implement it? I fear I'm missing something here.


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7135095@github.com>

errael

unread,
Sep 28, 2023, 11:24:16 AM9/28/23
to vim/vim, Subscribed

what this achieves

By defining an empty new() to start with, there's no default constructor, no unexpected consequences of changing the order of variable declarations. If I use a constructor that I intend to have, I get an error and can then change the empty constructor to what's needed.

obvious workaround is to declare explicit constructors

Yep, starting with an empty new() forces that.


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7135715@github.com>

dkearns

unread,
Sep 28, 2023, 11:55:31 AM9/28/23
to vim/vim, Subscribed

Thanks. No magic then. :)


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7136047@github.com>

errael

unread,
Sep 28, 2023, 12:01:20 PM9/28/23
to vim/vim, Subscribed

BTW, for small things I find the default constructor handy. Of course, somethings things don't remain small, so ...


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7136106@github.com>

dkearns

unread,
Sep 28, 2023, 12:08:31 PM9/28/23
to vim/vim, Subscribed

Using a "primary constructor" approach might be an improvement. It's explicit.

class A(this._x: number, this._y: number)
  this._z: number
  def newWithZ(this._x, this._y, this._z)
  enddef
endclass

A.new(1, 2)
A.newWithZ(1, 2, 3)


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7136210@github.com>

dkearns

unread,
Oct 30, 2023, 8:37:34 AM10/30/23
to vim/vim, Subscribed

  • add to help: when using a default new() method then reordering object members may cause trouble. Can define new() without arguments to avoid.

The fact that it needs to be documented with a warning seems reason enough to remove the feature. Is this "trouble" balanced by any purported convenience?

I'm not convinced the generated constructor is even applicable in the most general case. It seems most suitable for classes acting as records.

I think the easiest solution is to drop the feature and default to a nullary constructor. Other arguably better solutions include a primary constructor or named parameters. I note that Zimbu uses a simple nullary constructor..

Overall, I find this feature a bit gimmicky and counter to the aim of being "closer to commonly used programming languages".

@yegappan if you're not interested in changing this can you please just close it?


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7423520@github.com>

Yegappan Lakshmanan

unread,
Oct 30, 2023, 4:33:53 PM10/30/23
to vim/vim, Subscribed

If we replace the default autogenerated new() constructor which accepts all the member variables as arguments with an empty (or null) constructor, then to initialize the object variables to an initial value, the user needs to create a constructor method which will essentially be the same as the default constructor. For example, consider the following example (using the default constructor):

vim9script
class A
    this.n: number
    this.s: string
endclass

var a = A.new(100, 'abc')

This creates an object and initializes "n" to 100 and "s" to "abc" using the default constructor.

If we remove the default constructor, then the user needs to use the following to do the same:

vim9script
class A
    this.n: number
    this.s: string

    def new(this.n, this.s)
    enddef
endclass

var a = A.new(100, 'abc')

As you can see the user needs to now manually add the previously auto-generated default constructor.

Bram added the autogenerated default constructor to simplify this. But this suffers from the side-effect
that if the order of the member variables is changed, then the constructor method arguments also change.

Is using a generated default constructor method to initialize the object variables brings more value than
asking the user to manually add the constructor to initialize the variables?


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7428266@github.com>

errael

unread,
Oct 30, 2023, 5:26:20 PM10/30/23
to vim/vim, Subscribed

I wrote a response to this, and ended up turning it into enhancement request #13460. The bottom line is:

vim9script wants to be suitable for both simple scripting tasks and large scale projects; conflicting requirements. Over the last months the more I do simple things, the more I like the default constructor. If the default constructor goes away, I'll be disappointed but not heartbroken. As mentioned earlier, in larger projects I always define

def new()
enddef

, unless I forget, to avoid the default constructor.

Instead of pushing the language in one direction or the other, I'd say that vim9script development tools can alleviate a lot of issues associated with the language targeted at both tiny and large projects/tasks.


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7428583@github.com>

Christian Brabandt

unread,
Oct 31, 2023, 5:47:41 AM10/31/23
to vim/vim, Subscribed

I agree and I don't like the requirement of requiring the user having to write default constructors and I think the case that one is re-ordering arguments is happening far less often.


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7433213@github.com>

dkearns

unread,
Oct 31, 2023, 11:41:04 AM10/31/23
to vim/vim, Subscribed

Is using a generated default constructor method to initialize the object variables brings more value than
asking the user to manually add the constructor to initialize the variables?

I understand all the issues and trade-offs. I just came to the conclusion that it doesn't bring enough value.

It seems like the sort of feature that will cost some small number of users, particularly inexperienced programmers, ten minutes of head-scratching and a smaller number of users long-lived bugs. It made me think of Wat!.

As mentioned, I think some sort of primary constructor model solves all of these issues. This syntax is reasonably common, particularly in more recently developed languages.

Anyway, I don't want Ernie to be disappointed; it reminds me of my father.


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7436866@github.com>

errael

unread,
Oct 31, 2023, 4:26:23 PM10/31/23
to vim/vim, Subscribed

some sort of primary constructor model solves all of these issues

I don't understand what this is. Is it magic or something the user needs to create? What happens if members are reordered/added/deleted.


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7439438@github.com>

errael

unread,
Oct 31, 2023, 4:39:46 PM10/31/23
to vim/vim, Subscribed

the case that one is re-ordering arguments is happening far less often.

Lucky you.

I usually mix coding/design; that requires a liberal dose of refactoring to keep things clean. And when it's all done (as far as any software can be done), I like to spend a few days testing and more refactoring to get it in shape so that when I come back to it after months/years I can get up to speed quickly. I can protect myself from the default constructor, but there's other things that cause problems not seen with other languages/environments.


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7439524@github.com>

dkearns

unread,
Nov 2, 2023, 9:50:34 AM11/2/23
to vim/vim, Subscribed

I agree and I don't like the requirement of requiring the user having to write default constructors and I think the case that one is re-ordering arguments is happening far less often.

The issue isn't a trade-off between the two. In fact, the less frequently one reorders the more likely they are to shoot themselves in the foot, it only takes once. The issue is really how easy it is to become subconsciously reliant on the default constructor and forget about the unusual relationship.

I use the default constructor all the time for toy examples; I've stopped thinking about constructors in that context. It'll take quite a lot of experience to ingrain the fact that reordering field declarations requires updating constructor call sites so as not to make that bone-headed mistake in the wee small hours.

Can anyone name another language that has dependency between field declaration order and an auto-generated function (possibly excluding something generated from annotations)? [Not rhetorical]

I'll close this in a few days if there's no flood of support for removing it.


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7457090@github.com>

dkearns

unread,
Nov 2, 2023, 9:57:59 AM11/2/23
to vim/vim, Subscribed

Something like this, as mentioned earlier:

class A(this._x: number, this._y: number
)
  ...
endclass

A.new(1, 2)

Rather than a constructor created from fields, the fields are declared as part of the constructor. The point being that a reordering of the parameter list triggers the same obvious need for call-site changes as a reordering of any function's parameter list.


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7457172@github.com>

dkearns

unread,
Nov 2, 2023, 11:08:53 AM11/2/23
to vim/vim, Subscribed

It'd be the same as a manually defined constructor except for differences in class name vs "new" length (and excluding any extends/implements) It could elide the "this" too. It's shorter in lines too.

It's used by other languages that I like and haven't considered this a problem. It'd probably require a bit of thought on semantics around the edges though.

I quite like this, in the general case too, so might not be the most objective. :)


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7457997@github.com>

errael

unread,
Nov 2, 2023, 3:17:07 PM11/2/23
to vim/vim, Subscribed

Something like this, as mentioned earlier:

class A(this._x: number, this._y: number)
  ...

I wasn't sure whether or not that was an example language to express a point.

Oops, still confused

Rather than a constructor created from fields, the fields are declared as part of the constructor

looks like a class definition, not a constructor. Ah, the class defn auto-generates a constructor whether you want it or not.

reordering of the parameter list triggers the same obvious need ...

OK. So your foot is still a target, but it's more obvious that your foot's at risk.

And for a comment that only belongs here tangentially:
I'd rather have commands to

  • lint for "using default constructor"
  • generate constructor from fields
  • refactor: change function/method arguments


Reply to this email directly, view it on GitHub,.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/comments/7460360@github.com>

dkearns

unread,
Jan 12, 2024, 12:51:30 PM1/12/24
to vim/vim, Subscribed

Closed #13197 as resolved.


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/13197/discussion_event/1047613@github.com>

Reply all
Reply to author
Forward
0 new messages