Skip to content
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

Make object/class member variables public by default #12932

Closed
wants to merge 1 commit into from

Conversation

yegappan
Copy link
Member

To be consistent and simple, make the default access for class/object member variables public.
If a member variable name starts with an underscore, then it is private.

@codecov
Copy link

codecov bot commented Aug 27, 2023

Codecov Report

Merging #12932 (a255d33) into master (d809c0a) will decrease coverage by 3.88%.
Report is 22 commits behind head on master.
The diff coverage is 100.00%.

❗ Current head a255d33 differs from pull request most recent head d01a0df. Consider uploading reports for the commit d01a0df to get more accurate results

@@            Coverage Diff             @@
##           master   #12932      +/-   ##
==========================================
- Coverage   82.05%   78.18%   -3.88%     
==========================================
  Files         160      150      -10     
  Lines      194679   152275   -42404     
  Branches    43696    39254    -4442     
==========================================
- Hits       159743   119055   -40688     
+ Misses      22073    20971    -1102     
+ Partials    12863    12249     -614     
Flag Coverage Δ
huge-clang-none ?
linux ?
mingw-x64-HUGE 76.59% <100.00%> (-0.01%) ⬇️
mingw-x86-HUGE 77.08% <100.00%> (-0.01%) ⬇️
windows 78.18% <100.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Changed Coverage Δ
src/eval.c 91.83% <100.00%> (-0.62%) ⬇️
src/vim9class.c 85.75% <100.00%> (-3.64%) ⬇️

... and 147 files with indirect coverage changes

@errael
Copy link
Contributor

errael commented Aug 27, 2023

No way to declare a member read only? It's a shame to sacrifice safety for marginal simplicity. And with a performance hit thrown it; it's almost 4x slower using a Getter(). The main goal of vim9script is performance.

=== assign micro-prof ===
     50    100    200 : nLoops (usec/op)
  0.044  0.044  0.043 : var local = n_value            ###-A
  0.036  0.035  0.034 : var local = C.static_var       ###-D
  0.050  0.047  0.046 : var local = a_class.this_var   ###-G
  0.173  0.170  0.164 : var local = a_class.Getter()   ###-J

@yegappan
Copy link
Member Author

No way to declare a member read only? It's a shame to sacrifice safety for
marginal simplicity. And with a performance hit thrown it; it's almost 4x
slower using a Getter(). The main goal of vim9script is performance.

If we want to keep the object method and member accessibility consistent,
then we need this PR. Otherwise we can keep the current behavior.

@Shane-XB-Qian
Copy link
Contributor

Shane-XB-Qian commented Aug 27, 2023 via email

@Shane-XB-Qian
Copy link
Contributor

simplest way was just basic class which none (get rid) of those keyword, i'm ok with that, it's good enough for scripting.
or the most complex one: not only change control but also access control, adding 'readonly', and all control with keyword.

@errael
Copy link
Contributor

errael commented Aug 27, 2023

If we want to keep the object method and member accessibility consistent,
then we need this PR.

I don't understand that. Currently there is _ for private methods/members; public for for public. Nicely consistent. The issue revolves around what does it mean if there is no access modifier?

I thought read only member by default was elegant; it's simple to explain and offers both safety and performance. Here's some ways to look at it

  1. Require an access modifier, currently _ and public
    There is no default, and other access schemes, like introducing a readonly modifier for member, can be implemented in a source compatible way.
  2. Default is public, get rid of the public access modifier, it's not needed. If simplicity is the goal, it's clutter.
    Members/methods are public by default. Can introduce a readonly access modifier for current capability.
  3. Keep the current.
    Is it too complex/confusing that a method is not read only but by default a member is? members and methods are different; most languages don't have a concept of writing to a method.

@jessepav
Copy link
Contributor

I would definitely weigh in in favor of changing the current system, where an unadorned member is read-only, given that it's different from most (all?) other mainstream languages.

errael's second option (default public, _ private, readonly keyword) feels like the cleanest way to retain all current capabilities, though I personally wouldn't mourn the loss of readonly either, given that the semantics of vim9script's read-only members is unique as well.

@yegappan
Copy link
Member Author

If we want to keep the object method and member accessibility consistent,
then we need this PR.

I don't understand that. Currently there is _ for private methods/members;
public for for public. Nicely consistent.

The public keyword is used only for object/class members and not for methods.
The methods are public by default and private if the name starts with an underscore.

The issue revolves around what does it mean if there is no access modifier?

Yes.

I thought read only member by default was elegant; it's simple to explain and
offers both safety and performance.

With the latest code, you will get this behavior (without this PR). If this is the
desired behavior, then we don't need to make any further changes.

Here's some ways to look at it

  1. Require an access modifier, currently _ and public
    There is no default, and other access schemes, like introducing a
    readonly modifier for member, can be implemented in a source compatible
    way.
  2. Default is public, get rid of the public access modifier, it's not
    needed. If simplicity is the goal, it's clutter. Members/methods are
    public by default. Can introduce a readonly access modifier for current
    capability.

This PR introduces the behavior in 2 except for the readonly keyword.

  1. Keep the current.
    Is it too complex/confusing that a method is not read only but by default
    a member is? members and methods are different; most languages don't have
    a concept of writing to a method.

For 3, as described above, we don't need to make any further changes.

If we all agree approach 3 is acceptable (even though it is not consistent between
methods and members), then we don't need any additional changes.

Regards,
Yegappan

@errael
Copy link
Contributor

errael commented Aug 27, 2023

  1. Default is public, get rid of the public access modifier, it's not
    needed. If simplicity is the goal, it's clutter. Members/methods are
    public by default. Can introduce a readonly access modifier for current
    capability.

This PR introduces the behavior in 2 except for the readonly keyword.

The PR also takes out the internal support for readonly, rather than just at the parsing/specification level.

I was looking at what it would take to introduce a readonly keyword, before asking how adding that would be received. If this PR left the infrastructure for readonly, it would be a lot simpler to provide that capability.

@mg979
Copy link
Contributor

mg979 commented Aug 28, 2023

+1 for readonly and making members public by default (removing public altogether).

I don't think it's needed to make it an exclusive keyword, it can just be checked inside classes before member declaration, as other 'keywords' currently work (extends, implements, etc.).

@errael
Copy link
Contributor

errael commented Aug 28, 2023

Retain some parts of the read only member access code

Thanks. Assuming you're not planning to do read only, I'll take a look tomorrow. (What other plans?).

Idea is basically put back public using readonly; has_readonly instead of has_public. Tweak as needed and flavor with tests.

@errael
Copy link
Contributor

errael commented Aug 28, 2023

Oh, and readonly is the only one I've heard floated; haven't thought of anything else that's perfect. I could live with read or ronly. Once everything else is in place, it's simple to change the word.

@dkearns
Copy link
Contributor

dkearns commented Aug 28, 2023

readable...paint it green!

@errael
Copy link
Contributor

errael commented Aug 28, 2023

When I build objmember, with no modifications, and run this I see two problems

  1. Able to access private static
  2. Compile error when trying to write public static

Here's a test

vim9script
class C
    this._mpfoo: number
    static _spfoo: number
    this.mfoo: number
    static sfoo: number

    def new()
        this._mpfoo = 1
        _spfoo = 2
        this.mfoo = 201
        sfoo = 202
    enddef
endclass

def F()
    var o = C.new()
    #echo "_mpfoo" o._mpfoo
    echo "_spfoo" C._spfoo  ### <<<<<<<<< able to access private static
    echo "mfoo" o.mfoo
    echo "sfoo" C.sfoo

    o.mfoo = 301
    #C.sfoo = 302       ### <<<<<<<<< compilation fails
    echo "mfoo" o.mfoo
    echo "sfoo" C.sfoo
enddef
F()
finish

@errael
Copy link
Contributor

errael commented Aug 28, 2023

Oh, and this is what I'm running

 hg glog -r ::objmember -l 2
@  changeset:   18285:bdb16ee51e4c
|  bookmark:    objmember
|  tag:         default/objmember
|  tag:         tip
|  parent:      18253:025bc462c74c
|  user:        Yegappan Lakshmanan <yegappan@yahoo.com>
|  date:        Sun Aug 27 07:59:56 2023 -0700
|  summary:     Make object/class member variables public by default
|
o  changeset:   18253:025bc462c74c
|  user:        zeertzjq <zeertzjq@outlook.com>
~  date:        Sun Aug 27 11:17:39 2023 +0200
   summary:     patch 9.0.1792: problem with gj/gk/gM and virtual text

@errael
Copy link
Contributor

errael commented Aug 28, 2023

Has readonly ever worked? I just ran some tests with vim I built yesterday, standard vim9 stuff. Happily writes to read only variable.

@errael
Copy link
Contributor

errael commented Aug 29, 2023

I looked around to get a feel... The following fixes the "access to private static" var in the example, if you want to include it. Not tested much.

diff --git a/src/vim9expr.c b/src/vim9expr.c
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -434,14 +434,25 @@
     {
        // load class member
        int idx;
+       ocmember_T *m;
        for (idx = 0; idx < cl->class_class_member_count; ++idx)
        {
-           ocmember_T *m = &cl->class_class_members[idx];
+           m = &cl->class_class_members[idx];
            if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
                break;
        }
        if (idx < cl->class_class_member_count)
        {
+           // TODO: Would kindof prefer (@yegappan what do you think)
+           //       even though it's more likely to get a cache miss.
+           // if (m->ocm_access == VIM_ACCESS_PRIVATE
+           //                                      && !inside_class(cctx, cl))
+           // Maybe not, *name == '_' is simple to look at but...
+           if (*name == '_' && !inside_class(cctx, cl))
+           {
+               semsg(_(e_cannot_access_private_member_str), m->ocm_name);
+               return FAIL;
+           }
            *arg = name_end;
            return generate_CLASSMEMBER(cctx, TRUE, cl, idx);
        }

@errael
Copy link
Contributor

errael commented Aug 29, 2023

After the shock of finding out that readonly has probably never worked, the VIM_ACCESS_PRIVATE is certainly simpler than readonly. I wonder if checking where the various STORE instructions generation is called would do the trick? Or maybe compile_assignment. Is there any other way to change a variable? I wonder how [ x, y ] = [ 1, 2] is handled. generate_loadvar looks interesting.

I wonder if it is true that this can always be caught at compile time? Could a run time check ever be needed.

The parser/variable-state was simple/straightforward to get working. But the compiler seems to ignore it. Sigh.

@errael
Copy link
Contributor

errael commented Aug 29, 2023

I meant generate_store_lhs
But wait, this is only for classmembers so: ISN_STORE_CLASSMEMBER might be all it takes.

@vim-ml
Copy link

vim-ml commented Aug 29, 2023 via email

@vim-ml
Copy link

vim-ml commented Aug 29, 2023 via email

@errael
Copy link
Contributor

errael commented Aug 29, 2023

Can you open a PR for this with a test for this condition?

OK, but not tonight, tomorrow. I haven't looked at the "write public static compile error".

For my current work area, I took your objmember PR, transplant to work area, rebased to current master, added the readonly parse, I'll need to unwind and stash. I just saw compile_assign_unlet I believe that's the spot for the readonly check. The lhs_T looks like is has all the info needed for the check VIM_ACCESS_READ before any instructions are generated (3 for storing into the object when object is in local var).

A similar check is used before this block of code for
readonly object members

Assuming you meant private.

@Shane-XB-Qian
Copy link
Contributor

upd & link & refer : #11827

@errael
Copy link
Contributor

errael commented Aug 29, 2023

readonly is working AFAICT. I still need to put together some tests. I can't try writing to a readonly static because of the compilation failure bug that prevents writing to class statics. One nice thing about readonly is that it doesn't affect anything unless you use it.

I'm going to put this on the shelf while I put together the PR for preventing writing to private members.

The readonly PR is based on this PR. So this PR, #12932, has to go in before I can open a PR for readonly. Is this PR ready to go?

@dkearns
Copy link
Contributor

dkearns commented Aug 29, 2023

Is there some strong objection to requiring a keyword modifier? Why is a default needed at all?

We could require private and keep the required underscore as well. This is is already done for functions/methods which require an uppercase character.

The language uses strictly unnecessary keywords everywhere. In declarations, in particular, they're cheap and clear.

This would also obviate the problem with initialised declarations looking the same as plain assignments.

@errael
Copy link
Contributor

errael commented Aug 29, 2023

Is there some strong objection to requiring a keyword modifier? Why is a default needed at all?

Are you suggesting private/public for functions, members, static.

@errael
Copy link
Contributor

errael commented Aug 30, 2023

Other OOP languages have ways to define this behaviour...

I still think readable is a bad name, as is readonly for this. Maybe restrict or similar.

The discussion should be about whether or not the semantics should be available in vim9script.

I think it best to not discuss specific names, that derailed previous discussions.

@yegappan
Copy link
Member Author

To summarize, we have the following ways to declare and control access to object and class member variables currently (without the changes from this PR):

vim9script
class A
    # object member: read-only access
    this.val1 = 10
    # object member: read/write access
    public this.val2 = 20
    # object member: private (no access)
    this._val3 = 30

    # class member: read-only access
    static val4 = 40
    # class member: read/write access
    public static val5 = 50
    # class member: private (no access)
    static _val6 = 60
endclass

@Shane-XB-Qian
Copy link
Contributor

Shane-XB-Qian commented Aug 30, 2023 via email

@errael
Copy link
Contributor

errael commented Aug 30, 2023

@yegappan I appreciate that in your position you want to observe and let the discussion flow. But I would appreciate if you can find a way to express your opinions on the various topics. Not just state the facts.

How useful are read-only semantics? How big are projects going to get (I've never written vimscript or looked into the ecosystem, but I've seen projects grow and grow no matter the language). How important is safety (it is strongly typed...) and how do the available syntax/semantics affect that. What do you think about the performance hit. Anything else?

@mg979
Copy link
Contributor

mg979 commented Aug 30, 2023

Currently public isn't even needed by the way. I learned of it with this PR.

vim9script

class C
    this.str: string
endclass

def F()
    var tvar = C.new()
    echo tvar.str
    tvar.str = "a"
    echo tvar.str
enddef

F()

@yegappan
Copy link
Member Author

How useful are read-only semantics? How big are projects going to get (I've
never written vimscript or looked into the ecosystem, but I've seen projects
grow and grow no matter the language). How important is safety (it is
strongly typed...) and how do the available syntax/semantics affect that.
What do you think about the performance hit. Anything else?

I developed and maintain the following large and widely used Vim plugins:

https://github.com/yegappan/lsp
https://github.com/yegappan/taglist
https://github.com/yegappan/mru

Based on this experience, I can say that type safety and access control
are important. I have spent quite a bit of time optimizing these plugins
and found that reducing functions calls definitely helps. So having a
readonly access to the members will help.

@yegappan
Copy link
Member Author

Currently public isn't even needed by the way. I learned of it with this PR.

This is a bug. I will open a PR to address this.

@Shane-XB-Qian
Copy link
Contributor

Shane-XB-Qian commented Aug 30, 2023 via email

@chrisbra
Copy link
Member

okay, so I guess we should then leave it as it? Seems like that is prefered from the community point of view instead...

@Shane-XB-Qian
Copy link
Contributor

Shane-XB-Qian commented Aug 30, 2023 via email

@errael
Copy link
Contributor

errael commented Aug 30, 2023

Currently public isn't even needed by the way. I learned of it with this PR.

This is a bug. I will open a PR to address this.

@yegappan If we get this PR, #12932, settled along with what semantics for access will be supported, and a list of outstanding access control features that are problems and need fixing. Then I can assist; I've got some work done, but stopped when it became unclear what was needed.

Not sure the best way to enumerate the issues and at indicate who is working on what.

@errael
Copy link
Contributor

errael commented Aug 30, 2023

so I guess we should then leave it as it?

  1. merger this pr 2. make const and final work on class var. // then concern solved. // P.S: double or more leading underscore may should be an err.

    -- shane.xb.qian

@Shane-XB-Qian You might have a misunderstanding of the read-only semantics under discussion. In the vim9class docs it says

anybody can read, only class can write

The traditional const/final do not meet this criteria.

@errael
Copy link
Contributor

errael commented Aug 30, 2023

okay, so I guess we should then leave it as it? Seems like that is prefered from the community point of view instead...

@chrisbra Can you give a timeline on when this will be finalized. I'm unable/unwilling to work on issues until it's clear what needs to be done.

@errael
Copy link
Contributor

errael commented Aug 30, 2023

Currently public isn't even needed by the way. I learned of it with this PR.

This is a bug. I will open a PR to address this.

And specifically, I've got a fix that handles ACCESS_PRIVATE/ACCESS_READ in compile_assign_unlet. Which I think covers this particular issue.

@chrisbra
Copy link
Member

it's not clear to me what you want.

But to be clear, there is no rush, if you want something fixed, then just open an issue/PR and mention you are working on this. I am not in a hurry to release anything. We can tag this with the vim 9.1 Milestone and only when this is done get the release done.

For the Vim9 oop semantics, I trust you and Yegappan to let me know once you thing this is in a state that is ready to be released :)

And of course anybody else

@errael
Copy link
Contributor

errael commented Aug 30, 2023

it's not clear to me what you want.

The first thing is a decision on what access semantics are going to be supported in vim9script for the 9.1 release. That is still an open issue AFAICT.

The other problem is that access control was mostly not implemented at all as of last week. There's not been an issue opened. @yegappan and others have been addressing them as they come up; they have mostly been mentioned in unrelated PRs. There are/have-been to many specific cases to open an issue for each one (at least that's how it has been); but then again, I'm only saying what I've seen, accuracy might be off.

Maybe a discussion, with the access matrix, what's been tested, what works, ..., If someone wants to address a specific case, they could say so.

@Shane-XB-Qian
Copy link
Contributor

Shane-XB-Qian commented Aug 30, 2023 via email

@yegappan
Copy link
Member Author

Currently public isn't even needed by the way. I learned of it with this PR.

This is a bug. I will open a PR to address this.

And specifically, I've got a fix that handles ACCESS_PRIVATE/ACCESS_READ
in compile_assign_unlet. Which I think covers this particular issue.

Please feel free to open a PR with this fix and a test.

@errael
Copy link
Contributor

errael commented Aug 30, 2023

Currently public isn't even needed by the way. I learned of it with this PR.

This is a bug. I will open a PR to address this.

And specifically, I've got a fix that handles ACCESS_PRIVATE/ACCESS_READ
in compile_assign_unlet. Which I think covers this particular issue.

Please feel free to open a PR with this fix and a test.

Until there is a decision on access control syntax/semantics that's not going to happen (at least by me). So as long as you're not in a hurry... I'll do it when there's some decisions, otherwise it's potentially throw away code.

How should co-ordination, specifically about access control, be handled? It's currently a bit of a hodge-podge.

@Shane-XB-Qian
Copy link
Contributor

Shane-XB-Qian commented Aug 30, 2023 via email

@chrisbra
Copy link
Member

Maybe a discussion, with the access matrix, what's been tested, what works, ..., If someone wants to address a specific case, they could say so.

Feel free to open as discussion. I am not in the position to test this currently.

@errael
Copy link
Contributor

errael commented Aug 30, 2023

Maybe a discussion, with the access matrix, what's been tested, what works, ..., If someone wants to address a specific case, they could say so.

Feel free to open as discussion. I am not in the position to test this currently.

@yegappan You're the vim9script lead in this benign dictatorship. Whatever, if anything, we do must have your blessing or it's not worth doing.

@chrisbra Is there a timeline for when a decision will be made? Nothing to do or work on if there's no target.

@errael
Copy link
Contributor

errael commented Aug 30, 2023

Just in case there is a doubt. I would favor closing this PR without merging as described by @yegappan in the comment below extracted from #12932 (comment)

To anyone who wishes to make clear their preference, keeping in mind that this is not a vote. Please be brief. There has been plenty of discussion.

to continue to support the existing readonly default access,
[...]
We then don't need the changes in this PR and can keep
the existing behavior.

@yegappan yegappan closed this Aug 31, 2023
@Shane-XB-Qian
Copy link
Contributor

Shane-XB-Qian commented Aug 31, 2023 via email

@yegappan
Copy link
Member Author

Maybe a discussion, with the access matrix, what's been tested, what works, ..., If someone wants to address a specific case, they could say so.

Feel free to open as discussion. I am not in the position to test this currently.

@yegappan You're the vim9script lead in this benign dictatorship. Whatever, if anything, we do must have your blessing or it's not worth doing.

I have started a discussion (#12979) to capture the
access matrix. I have tested the various access controls for an object method and the
different ways it can be accessed. All of these works as expected. Let me know if I missed
any cases.

Accessing a class member variable from a def function currently doesn't work.

@errael
Copy link
Contributor

errael commented Aug 31, 2023

I have started a discussion (#12979) to capture the
access matrix.

Thank you. I'll take a look. I would have done something if you'd said "go ahead" .

@vim-ml
Copy link

vim-ml commented Aug 31, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants