Godot-proposals: Add public/private access modifiers to GDScript and autocompletion improvements

Created on 27 Mar 2020  路  29Comments  路  Source: godotengine/godot-proposals

Describe the project you are working on:
2.5d beat'em up

Describe the problem or limitation you are having in your project:
Inside a GDScript class i often use "private" variables and functions that shouldn't be visible ouside but that instead are "public", hence visible and usable by everyone.
More or less related is the fact that autocompletion is cluttered, yes alphabetic order is good but the list always starts with lots of constants and the function or variable we want is usually buried in lots of other things that aren't useful in that moment.

Describe the feature / enhancement and how it helps to overcome the problem or limitation:
Let us specify if a variable or function is private or protected(or whatever keywords you want to use), a private variable can only be used by the functions of the same class, protected means that it's also accessible by the classes that inherit from the class the variable is in.

About autocompletion in my opinion it would be even more useful if suggestions were grouped by class, ordered from the leaf of the class tree back to the root class, or maybe add to the editor's options the possibility to order and filter what is shown.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
var v(no access modifier means public)
protected var v(accessible only in the class it's in and the ones that inherit it)
private var v(accessible only in the class it's in)

Autocompletion suggestions would be grouped by class, ordered from the leaf to the root of the class tree, maybe with their class written somewhere.
It would be also helpful if the suggestions were colored the same way as in the editor, this way it would be even faster and easier to find what we want.

If this enhancement will not be used often, can it be worked around with a few lines of script?:
Access modifiers and autocompletion are essential features so they would be used very often and probably they can't be made with few lines of script.

Is there a reason why this should be core and not an add-on in the asset library?:
It seems to me that a GDScript feature like these ones should be core.

gdscript

Most helpful comment

Totally agree, public by default, but I really want to be able to mark things as private.

All 29 comments

I'm going to repeat myself but, I've always found GDScript to be very similar to C++. Yeah I know it's a weird statement but it just the way it feels, especially with static typing being a thing now. In fact when I ported some of the C++ classes back to GDScript, I had to make mostly minimal changes in order for those classes to work. Allowing access modifiers is just a natural step to making GDScript feel like C++ on the mental level. I'm mostly biased as a Godot modules developer, where I dedicate ~30% of development time to C++ programming.

As an experienced programmer just starting to learn godot idly, I'm also bouncing off this as a vague irritation.

I'm fine with 'public by default', and honestly prefer it, but not having a way of making this 'private' makes it more difficult 'remember' how to use the code I've written later. And yes, you can _underscore things but they still appear visible and clutter the autocomplete list even further. :(

this would allow encapsulation which I support
however what about marking nodes as private, protected and public in the editor too?
this way people can hide nodes for modding

Totally agree, public by default, but I really want to be able to mark things as private.

I'd like to be able to have a variable with private setter and public getter.
Or the other way around.

There's one detail i'd do differently: i wouldn't use the verbose syntax like the one in C# but rather the less verbose syntax of C++

public:     # can be omitted since it's the default
var all
var my
var Public
var vars

private:
var All
var My
var Private
var Vars


protected:

func _init():
    pass

func _ready():
    pass

public:

func my_public_function():
    pass

To avoid typing extra keyword before every single var/func. It works better when the code is grouped by access modifier which is recommended by GdScript Style Guide.

@hilfazer I'm not really fond of disconnecting the access modifier from the variable/method name. It makes it harder to visually check an individual member's access modifier. Also, if you copy-paste a snippet around, the access modifier will be lost.

@hilfazer I'm not really fond of disconnecting the access modifier from the variable/method name. It makes it harder to visually check an individual member's access modifier. Also, if you copy-paste a snippet around, the access modifier will be lost.

I didn't really had your first issue during a couple of years working with C++ except when a class was trying to do to many things and therefore was too big.

I'm not at all surprised you're saying this. You're a Godot developer and have to deal with classes like
https://github.com/godotengine/godot/blob/3.2/modules/gdscript/gdscript_parser.h
or
https://github.com/godotengine/godot/blob/3.2/core/class_db.h

As for you second issue - sometimes it's undesirable and sometimes it's not.
If i have a public function i want to move to my private section i totally want it to loose access modifier.

I am very much in favour of having access modifiers in gdscript.
all public is fine for small games / prototypes, but for more serious projects the absence of those become a burden.

also, the good old SOLID principles are virtually impossible without access modifiers.

also, the good old SOLID principles are virtually impossible without access modifiers.

You can have de jure access modifiers via annotations or conventions and still obey SOLID principles. They don't have to be enforced by the compiler (that's how I see it at least).

private methods are vital when creating plugins.
I also vote for the implementation of the protected methods in gdscript

For me the main task of private and protected methods is that the method cannot be called from outside.

Until access modifiers are implemented in gdscript, just came up with a work-round, what do you think about this?
this makes do_safely() kinda protected

extends Node
class_name DoSafely

var key setget noset, noget

func noset(val):
    pass

func noget():
    pass


func do_safely(the_key):
    if the_key != key:
        print ("you shall not pass!")
        return

    print ("hello")


func _init():
    randomize()
    key = randi()

GD script definitifly needs such option to mark a class/function/members as private/proteced or by default as public.
I'm current developing an plugin and all my classes are listed in the available class list. This is unwanted because this classes only internall stuff to work within the plugin.

A Project will be unmaintainable when the amount of classes increases.
Also the fact to prevent direct access to members is very important.
To only using a prefix "_funcname" not prevents others to use this functions/members.

Please add private and protected modifiers to can manage such stuff, this will help a lot.

I prefear to use like in common languages

private func foo():
protected func bar()
# or 
func_private foo():
func_protected bar()

like the 'static' keyword
same for classes e.g.
````

private class not visible outside the package and not able to inherit from

class_name_private Foo
extends Resource
..

protected class not visible outside the package

class_name_protected Foo2
extends Resource
..

public class

class_name Bar
extends Resource
````

Best regards Mike

if this was implemented I think the best way to go about it is make everything protected by default. that way people can choose what they want to expose.

if this was implemented I think the best way to go about it is make everything protected by default. that way people can choose what they want to expose.

I don't think you want protected for most of the methods and properties. At least you wouldn't want this for most utility methods and singleton methods and properties. Otherwise, the codebase would be full of public keywords.
It would also require a more thoughtful code organization and architecture, which would be difficult for beginners to do.

@me2beats agreed, I think public by default makes the most sense. It gives beginners the save experience as today.

I strongly disagree.
Then you have a bunch of members you might not want modders to use.
protected is there for the purpose of the derived object to use and then choose what to expose.
if you expose everything your taking away control from the developer which is not people want when creating their own games

EDIT: also I understand thinking about beginners, but godot tends to take away control from the user to make it simple for beginners. and while making it easier for someone to use is good, taking away control is worse.
and I honestly think this is one of godot's biggest flaws right now.

@Shadowblitz16 I'm saying public by default, but with private and protected as explicit options.

@Shadowblitz16 public should be the default, private and protected be optional

I really do want to have private members.

My biggest problem is the following:

class ParentClass:
  var a = 10
  var random_var = a/2 # I want to make this private

class ChildClass extends ParentClass:
  var random_var # throws an error, since it already exists in parent class.
  # I don't want to limit all the child classes just because I needed a helper variable for something

Also, it would be great if I could make a variable private in the following example as well.

class Enemy extends Node2D:
  var max_health : int = 100
  var health := max_health setget _health_changed
  var health_percent : float = 1.0 # This is the variable I want to make private

  func _health_changed(val : int):
    health = val
    health_percent = float(health) / max_health

  func _process(delta):
    scale.y = health_percent
    # Other uses of health_percent

This health_percent variable should not be accessed by its children. And I would like to make sure it isn't.
Also, it would show up in the auto-completion, which is just annoying.

I think something like this should be done..

#allow accessing friend members in Sprite and AnimationPlayer
extends Node2D friends Sprite, AnimationPlayer

pub var my_public_variable
pro var my_protected_variable
pri var my_private_variable
fri var my_friend_variable

pub func my_public_function(): pass
pro func my_protected_function(): pass
pri func my_private_function(): pass
fri func my_friend_function(): pass

It seems more pythonic since the function and variable keywords are shortened I don't see why these can't be
its also easier to type

I think something like this should be done..

#allow accessing friend members in Sprite and AnimationPlayer
extends Node2D friends Sprite, AnimationPlayer

pub var my_public_variable
pro var my_protected_variable
pri var my_private_variable
fri var my_friend_variable

pub func my_public_function(): pass
pro func my_protected_function(): pass
pri func my_private_function(): pass
fri func my_friend_function(): pass

It seems more pythonic since the function and variable keywords are shortened I don't see why these can't be
its also easier to type

I'm not really against this one... But I'm not sure about the readablity either.
I understand that with a long line like this it might help

pub export(int) onready var things

vs

public export(int) onready var things

But it's already kind of long.
I'm nore on the side of typing these out.

However I still like the C++ style the most. Although people don't seem to like it as much.
But that would decrease the typing the most, and with some indenting it's even readable.

That being said, if this was the final choice, I wouldn't mind that much...

You can enforce access modifiers and encapsulation in GDScript with one cool little trick: using Inner Classes! Here's an example:

# MyClass.gd
extends BaseClass

# Public variables and methods
var  value_a : int
var  value_b : int

func method_a():
    # do something
    # ...
    pass

# Private variables and methods <--- here is where it gets interesting
var   _m
class Private:
    var  value_c : int
    var  value_d : int

    func method_b() -> void:
        # this function is hidden from outside access
        # ...
        pass

# Constructor
func _init():
    _m = Private.new()

# You can use public and private variables and methods as such:
func _process( delta ):
    value_a = 1
    value_b = 2
    method_a()

    _m.value_c = value_a + value_b
    _m.value_d = _m.value_c + _m.value_c
    _m.method_b()

Since the contents of Private are internal to MyClass and the type of _m is unknown until runtime, all variables and methods within Private will _not_ be exposed to other objects; they won't appear in the class definitions and they won't appear in autocomplete, which makes it perfect for plugins and modules. Private also won't be able to be inherited from.

If you want to offer a way to access these private variables externally without exposing implementation, you must define a public interface:

func get_value_c() -> int:
    return _m.value_c

This way, GDScript works pretty much as any other language with enforced access modifiers.

By the way, I personally prefer to use _m instead of private because in my opinion it makes the code more readable, elegant, and less verbose:

var   _m
class Private:
    var  velocity_prev : Vector3
    var  velocity_curr : Vector3
    var  position      : Vector3
    var  acceleration  : float = 3.0 # m/s虏

    func integrate_taylor_series() -> void:
        # this function is hidden from outside access
        # ...
        pass

func _init():
    _m = Private.new()

func _physics_process( delta ):
    _m.velocity_prev  = _m.velocity_curr
    _m.velocity_curr += _m.acceleration * delta
    _m.position += ( _m.velocity_prev + _m.velocity_curr ) * 0.5 * delta
    _m.integrate_taylor_series()

I just saw some code on Discord which reminded me of this issue. The code:

GodotLotsOfOnreadyVars

Imagine putting private or protected in front of each of these declarations.

Imagine putting private or protected in front of each of these declarations.

This is an instance where https://github.com/godotengine/godot-proposals/issues/641#issuecomment-642488226 would make it more readable.

My stance on access modifiers is that they are not necessary for following OOP standards (In my time using Godot I never had a case where I needed them. They only add visual noise, the information they provide can be easily added by using underscores:

private var health = 10 setget set_health
public var name = "name"

private var inventory = []

public func set_health(to):
  health = to

vs

var health = 10 setget set_health
var name = "name"

var _inventory = []

func set_health(to):
  health = to

One actually useful keyword would be virtual, which would notify you if you didn't override a certain method.

One actually useful keyword would be virtual, which would notify you if you didn't override a certain method.

This can be implemented using a @virtual annotation. I think the same should be done with @private eventually.

Too many annotations are a bad thing, it makes code ugly and unreadable.
Access modifiers and things like virtual and abstract should not be annotations.

Was this page helpful?
0 / 5 - 0 ratings