Skip to content

Class Constructors#210

Open
andyfriesen wants to merge 1 commit into
masterfrom
class-constructors
Open

Class Constructors#210
andyfriesen wants to merge 1 commit into
masterfrom
class-constructors

Conversation

@andyfriesen

@andyfriesen andyfriesen commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Introduce class constructors via special .__init and .new() methods.

Rendered

@TenebrisNoctua

Copy link
Copy Markdown

Honestly I'm kind of torn by this RFC, it defines a lot of new rules and restrictions just to work around the problem that comes with inheritance, can't a static keyword be implemented in its place instead to allow a class to only define a static field which will not be effected by inheritance? The developers will have to keep in mind of the rules when defining constructors, and that may confuse some people within the ecosystem.

@andyfriesen

Copy link
Copy Markdown
Collaborator Author

Honestly I'm kind of torn by this RFC, it defines a lot of new rules and restrictions just to work around the problem that comes with inheritance, can't a static keyword be implemented in its place instead to allow a class to only define a static field which will not be effected by inheritance?

Could you explain how a static keyword could help? I don't understand.

The restrictions exist to ensure that you can't interact with an object that isn't fully constructed yet. If you write a field public t: thread, it isn't actually a thread until you initialize it.

@deviaze

deviaze commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

This adds too much complexity to the language, comes with a bunch of restrictions and rules you need to know to use properly, and isn't obvious.

An __init function that gets called as new is confusing, I just want to write a new function like in Rust and be done with it.

Inheritance is a controversial feature that's discouraged in modern language design, and I don't think that supporting it like this is the right choice for Luau. Has a different approach like composition been investigated?

@TenebrisNoctua

Copy link
Copy Markdown

Honestly I'm kind of torn by this RFC, it defines a lot of new rules and restrictions just to work around the problem that comes with inheritance, can't a static keyword be implemented in its place instead to allow a class to only define a static field which will not be effected by inheritance?

Could you explain how a static keyword could help? I don't understand.

The restrictions exist to ensure that you can't interact with an object that isn't fully constructed yet. If you write a field public t: thread, it isn't actually a thread until you initialize it.

My comment on static was more or less about the first section of the RFC, which brings up the problem with inheritance for the .new function.

A static access modifier would bring in the capability of defining static methods/fields for a class, which cannot be inherited by a subclass. This would solve the problem with each class having their own non-inherited static constructor function.

Assuming functions with static keyword (or no self argument) are static and cannot be inherited, doesn't this sound like a good way of allowing the user to create their own constructors without reserving any names:

class Base
    public foo: string
    static function new(newObj, str: string)
        if not newObj then
            return Base { foo = str }
        end
        newObj.foo = str
    end
end

class SubFromBase extends Base
    static function new()
        local newObj = SubFromBase { } -- Assuming this is allowed, you move the magic from __init to here
        Base.new(newObj)
        -- Add and initialize other properties as you would
    end
end

For every child of SubFromBase must define their own static constructor function that may call SubFromBase, which calls Base.


## Alternatives

We could follow in Python's footsteps and do away with the default `T{}` constructor, but this means that developers have to write a lot of dull code in the typical "plain old data" case:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would actually prefer this even more, as the way "default table constructors" for classes currently work really irritates me.

First, as I've said before, the current design conflates the idea that the fields of a class are also its constructor. This poses awkward questions regarding how private and static fields are treated and is just a big mismatch with the current syntax. Languages that lay out their class fields in a spreadsheet manner make the programmer write out the constructor by hand, but Luau doesn't and generates one automatically, which is weird.

Second, the table constructor protocol requires at a bare minimum to allocate a throwaway table that is then passed to a C function that then sanitizes and allocates the real object from it. This implies that baseline performance for construction will always be worse than just allocating a regular table. The justification that future compiler heroics will cut back on the performance hit feels more like a band-aid solution, and I'd expect a stronger foundation for the runtime side than that.

Third, this RFC is proposing an alternative constructor protocol that supersedes most if not all uses for the default table constructor. If that's the case, why even support table construction to begin with? If someone feels like the constructor boilerplate is "pointless ceremony" for a POD datatype, then maybe the syntax for classes needs to be revisited to incorporate the idea of default constructors better.

@TenebrisNoctua TenebrisNoctua Jun 4, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I think a C++ like syntax would be more ideal, where the class object can still be called like a function, however you do not allocate a table for initializing the fields. Instead, you pass arguments to this function, which if defined, will pass these arguments to a constructor. If there is no constructor, then the fields will not be initialized, and therefore nil.

class Foo
    protected x: number
    public function Foo(self)
        self.x = 51
    end
end

class Bar extends Foo
    public function Bar(self)
        Foo(self)
    end
end

local newFoo = Foo()
local newBar = Bar()

Foo() now applies magic (the same in the proposed __init) and constructs a new, but uninitialized instance of the class. This instance is then passed to the constructor, in which the function can use to initialize its fields.

However, this constructor can also be called with another object which is made within a subclass, in which the constructor's self parameter points to this object, rather than a new instance of Foo.

One problem with this could be that there is an ambiguity between the argument types. To solve that problem, Luau could check whether or not the first argument to the class function is an object which is made from a subclass.

class Foo
    protected x: number
    public function Foo(self, x: number)
        self.x = x
    end
end

class Bar extends Foo
    private y: string
    public function Bar(self, x: number, y: string)
        Foo(self, x)
        self.y = y
    end
end

local newFoo = Foo(51)
local newBar = Bar(51, "Hello")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's sort of what I had in mind as well, minus the inheritance bit. Anything but a default table constructor so that the class can be truly encapsulated should private and static fields be added.

And for POD cases, "record"-style syntax sugar could be added which generates a constructor automatically, i.e. class Point(x: number, y: number) end.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels similar to the proposal in this RFC, but with better semantics and you can still use .new() however you please. I wonder if this is worthy of an amendment RFC, or should be included in this RFC instead.

@Bottersnike

Copy link
Copy Markdown

I don't understand the rationale with __init and new. I can understand the name __init to follow metamethod-style, but in that case I would expect a "special" way to initialise, such as MyClass(...) calling __init. Having new magically mapped feels.. off. This RFC expressly disallows new being defined as a function, but then has users define new anyway, just writing "new" as "__init".

It feels like in the battle between two options (__init->T() or new->.new()) the worst middle-ground compromise has been picked here.

We draw inspiration from Python

B.__init(self, x)

Something worth noting is Python relatively quickly (in the scheme of python development times) realised this wasn't ideal, and ended up with super().__init__(...). Given we've got the benefit of hindsight, it could be worth designing something akin to this in from the start?

The first argument of a constructor must be self.

Are we walking a slippery road with starting to make variable names formally required? If the first argument to a function must be named self would it not make more sense to entirely omit it, and have it injected in (akin to a namecall)? Alternatively, allow it to be named anything of the user's choosing. The middle ground of an enforced variable name in a function argument seems particularly weird.

If the base class defines a constructor, the class constructor must invoke it before reading or writing to self or any of its properties

This seems hard to enforce? Is there a reason it's required? The base class would be assuming an empty table, so isn't going to be reading from fields anyway? If default field values happen, it feels like we have added value by allowing a subclass to overwrite the already-initialised defaults before calling a base constructor? I do agree most uses would invoke the superclass constructor as the first step of their constructor, but is it a required restriction?

As a special exception, fields whose types are supertypes of nil are exempt from this requirement and are always considered to have been implicitly initialized with nil. Explicit initialization of such fields is of course permitted.

This almost points towards "we have default values, but the only default you can assign is nil". This of course comes part-and-parcel with the semantics of Lua(u) tables, but strikes me as a call-out that indicates a wider lack of defaults? It might almost, at least to me, make sense to not have this exception, and require optional fields to either be explicitly initialised as self.foo = nil or defined as foo: number? = nil in the declaration.

I only raise this because this is the sort of exception that shouldn't really be walked back once released (though as I understand it this enforcement is only via types, so absolutely could have "breaking" changes).

--

In terms of general implementation inheritance, how does __init interact with inherited methods/metamethods? Is __init inherited like an ordinary method, or is it special-cased as constructor-only? Metamethod inheritance was not covered in #209, so it is unclear whether this relies on metamethods not being inherited, or whether __init has bespoke rules.

Also, if a subclass defines no new fields and does not need additional initialization, must it still define a constructor that simply delegates to the parent constructor?

@TenebrisNoctua

Copy link
Copy Markdown

I don't understand the rationale with __init and new. I can understand the name __init to follow metamethod-style, but in that case I would expect a "special" way to initialise, such as MyClass(...) calling __init. Having new magically mapped feels.. off. This RFC expressly disallows new being defined as a function, but then has users define new anyway, just writing "new" as "__init".

It feels like in the battle between two options (__init->T() or new->.new()) the worst middle-ground compromise has been picked here.

We draw inspiration from Python

B.__init(self, x)

Something worth noting is Python relatively quickly (in the scheme of python development times) realised this wasn't ideal, and ended up with super().__init__(...). Given we've got the benefit of hindsight, it could be worth designing something akin to this in from the start?

The first argument of a constructor must be self.

Are we walking a slippery road with starting to make variable names formally required? If the first argument to a function must be named self would it not make more sense to entirely omit it, and have it injected in (akin to a namecall)? Alternatively, allow it to be named anything of the user's choosing. The middle ground of an enforced variable name in a function argument seems particularly weird.

I don't think the first parameter must always be named self, but rather something that points to the object. Should've been made more clear to be honest.

I pretty much agree on the other points, incredibly weird and complex workaround just to achieve constructors and support inheritence. Semantics could've been so much better if table initialization did not exist and instead of new, only the class name as the constructor was reserved. Instead of Python, we could follow C++.

@InfraredGodYT

Copy link
Copy Markdown

I also think that mirroring Python isn't the way to go. __init looks like a metamethod rather than a constructor. Not to mention the weird split in syntax between classes that have constructors and classes that don't.

-- Has constructor:
T.new(...)

-- Doesn't have constructor:
T { x = 1, y = 2 }

I'm also not sure why constructor and super weren't mentioned in the Alternatives section.

@TenebrisNoctua

TenebrisNoctua commented Jun 5, 2026

Copy link
Copy Markdown

I've noticed that the class instance initialization method in POD does not work well with constructors, especially with inheritance. Because of this, I'd like to propose alternative syntax and semantics for constructors and class instance initialization.

While this requires possibly amending the already existing class syntax, it could be better for the feature overall.

Instead of Foo { ... }, the user initializes the properties within constructors. This means that Foo() should now be used instead to construct an uninitialized instance of a class.

Foo() also now gains two behaviors:

If there is no constructor (__init) present:

class Foo
    public x: number
end

local newFoo = Foo() -- If no constructor exists, this initializes a new uninitialized instance of the class Foo.
newFoo.x = 1

If there is a constructor:

class Foo
	public x: number
	
	function __init(self, x: number)
		self.x = number
	end
end

local newFoo = Foo(1) -- This calls the constructor with the parameters

If there exists a constructor defined, Foo() will now call the constructor with an already created, albeit still uninitialized instance of class Foo.
While this does introduce magic, it removes the table allocation problem completely from classes, and gives user total control over initialization.

For inheritance

In C++, parent class constructor is automatically called before the derived class constructor, if they don't require any additional arguments.
Instead of following Python, perhaps C++ can be followed instead, and in many cases this could solve our parent class constructor problem.

class Base
	function __init(self)
		print("Base class called!")
	end
end

class Derived extends Base
	function __init(self)
		print("Derived class called!")
	end
end

local newDerived = Derived()
-- The output would be:
-- "Base class called!"
-- "Derived class called!"

Of course, when it comes to passing parameters to parent class constructors, things get a little bit tricky, as in C++, you can define an initializer list, however to do that in Luau, additional syntax would have to be implemented.
The RFC states that additional syntax is not desired when we already are using a lot, so this may not be desirable.

We could still move on with the rule that if the base class defines a constructor, the class constructor must invoke it before reading or writing to the object or any of its properties:

class Base
	public x: number
	function __init(self, x: number)
		self.x = x
	end
end

class Derived extends Base
	function __init(self, x: number)
		Base.__init(self, x)
	end
end

local newDerived = Derived(1)

We could also re-consider super, which in Python allows the user to reference the methods of the parent class, without explicitly needing to directly refer to the parent class.

class Base
	public x: number
	function __init(self, x: number)
		self.x = x
	end
end

class Derived extends Base
	function __init(self, x: number)
		self:super().__init(self, x)
	end
end

local newDerived = Derived(1)

super could also just directly refer to the parent method/constructor like:

class Base
	public x: number
	function __init(self, x: number)
		self.x = x
	end
end

class Derived extends Base
	function __init(self, x: number)
		self:super(x)
	end
end

local newDerived = Derived(1)

Overall, I believe these proposed syntax changes would make this RFC more solid, and would make writing classes with inheritance and constructors a lot easier. It may also remove the table allocation problem, which would be a nice bonus.

@Bottersnike

Copy link
Copy Markdown
local newFoo = Foo() -- If no constructor exists, this initializes a new uninitialized instance of the class Foo.

This feels like really unintended behaviour. newFoo.x is typed as number, but is now actually nil!? I think it might be better to instead have a "default constructor" that's just the POD thing we have right now, because that way we never get into a situation where an object doesn't match the types.

Regarding inheritance, the first example given is imo untenable because of the exact issue raised there: constructor arguments. I think having to call Base.__init() is definitely the better option.

I also agree with the addition of super, but how it would be done I'm not sure. self:super() isn't really viable unless we reserve the method name super, but that feels like a nasty workaround. class.super(self).__init() could work, though is potentially a bit wordy?

I think super, in whatever shape it takes, should refer to the parent class, not directly to its constructor, because then it can also be used to call parent implementations of methods that have been overridden in a child.

@TenebrisNoctua

TenebrisNoctua commented Jun 5, 2026

Copy link
Copy Markdown
local newFoo = Foo() -- If no constructor exists, this initializes a new uninitialized instance of the class Foo.

This feels like really unintended behaviour. newFoo.x is typed as number, but is now actually nil!? I think it might be better to instead have a "default constructor" that's just the POD thing we have right now, because that way we never get into a situation where an object doesn't match the types.

Honestly this could be avoided if we had default values for fields. As mentioned in the RFC, in certain cases types may not match the values, especially when the self argument points to an uninitialized object, which means x will be nil regardless. Perhaps another behavior could be found where we still do not have to allocate a table for initialization.

I also agree with the addition of super, but how it would be done I'm not sure. self:super() isn't really viable unless we reserve the method name super, but that feels like a nasty workaround. class.super(self).__init() could work, though is potentially a bit wordy?

super could also be a contextual thing, because class.super(self)._init() is very wordy. And adding it under class library would be weird.

@Bottersnike

Copy link
Copy Markdown

One challenge with super that just came to mind is the types. Given I don't think the plan is to have some magic base object type (a-la python, roblox itself, etc), the superclass of a class that doesn't inherit anything would be nil. That means that instead of super().__init(), and in the absence of safe navigation, we'd need it typed having an optional return, leading to

local sup = super()
if sup then sup.__init() end

I unfortunately don't have a good solution for that in mind. The base object type idea is quite reliable but probably undesired. One option would be to make it error, instead of returning nil. I'm unsure to what degree this would be desired.

--

In terms of class.super, one power it would provide as a member of class is being able to inspect the inheritance chain of something; this could be useful?

@TenebrisNoctua

Copy link
Copy Markdown

In my own class system, super() errors if the class does not have a superclass. Not sure if people would desire this like you have mentioned, but erroring could be better than nil.

That aside, now that I'm thinking about it, if we had super under the class library, semantics could be better.

const super = class.super
class Base
    function __init(self)
    end
end

class Derived extends Base
    function  __init(self)
        super(Derived).__init(self)
    end
end

Still unsure though.

@Cooldude2606

Copy link
Copy Markdown

Classes that have constructors cannot be constructed via T() syntax. Instead, the static method .new must be invoked. Classes that do not define constructors are still initialized via T {...} syntax.

This is a very confussion restriction to impose with no justification presented. I would instead propose that using T {...} syntax ("table call") calls a default __init method which can be optionally replaced with a new implimentation. With metatables it might have looked something like the following:

-- T { ... }
function class_mt.__call(class, ...)
    local self = {}
    class.__init(self, ...)
    return self
end

-- T.__init(self, args)
function class.__init(self, args)
    self.x = args.x
    self.y = args.y
end

This maintains the current behaviour of using a table call to create a new instance of a class while permitting new implimentions to extend behaviour. There is no requirement for this implimentation to use table calls as one may instead use __init(self, x, y) and call via T(x, y); although if defaults with table calls are optimised (which I see no reason why they would not be, see last paragraph) then this parttern would become a performance footgun because of the additional function call.


In terms of typing for super classes, I think class.super would be nice but can be implimented in a different RFC. It will be sufficent at the moment to call the parent class directly within class methods, with "super" being a useful QoL addition to be agreed on later. Although I will chip in and say I agree that it should error if the class has no parent, and any external introspection can wrap it in pcall. local hasSuper, super = pcall(class.super, self)


Assuming the above, I want to suggest that any replacement init method must refine the original self argument into a form which fulfils the class type, or else results in a lint error. There is no requirement that the parent init is called; however, if it is not, then the child init is now responsible for completing the initialisation of the member fields. Additionly, there is no restriction on reading or writing of any member fields, but they will be typed as nil-able if they have not been refined by the parent init. Apologies for the use of typescript, luau has no "assert type predicate", but I would imagine it as the following:

function __init<
    Self extends Partial<{ x: number; y: number }>
> (
    self: Self,
    args: { x: number; y: number },
): asserts self is Self & { x: number; y: number };

I saw there was another RFC which suggested the addition of type predicate functions, and so that may need to be revisted to enable this kind of type refinement on arguments. But anyway, it would work something like so within lua:

class Point2D
    public x: number
    public y: number
    -- no __init implimented, the default is used, which matches the metatable example above
end

class Point3D extends Point2D
    public z: number
    
    function __init(self, args: { x: number, y: number, z: number })
        -- self: { x: number | nil, y: number | nil, z: number | nil }
        Point2D.__init(self, args) -- It is still callable as a default is generated or inlined
        -- self: { x: number, y: number, z: number | nil }
        self.z = args.z
        -- self: { x: number, y: number, z: number }
        -- because self is the correct type, no lint error is rasied about incomplete field init
    end
end

As an extension, it is possible that Point3D.__init could also be seen as “default”, which is to say: a call to a default parent init where you pass your args, then extract your new members from the table. This would allow for simple heirarchries of classes without needing to specify any custom implimentations. Also, because it is suggested that child classes have their fields ordered the same as the parent, it would be trivial for a full chain of defaults to be inlined without any table creation or function calls at all. This maintains the current optimisation that could be applied to current class construction, so long as no class in the heirarchery have a custom implimention of init, in which case it would need to fallback to compiling as a function call to init. Some made up byte code as food for thought on how defaults could compile:

Unoptimised bytecode where __init is never inlined
; --- call site: constructing with Point3D { x = 1, y = 2, z = 3 }  ---

[1] LOADK           0 1            ; x = 1
[2] LOADK           1 2            ; y = 2
[3] LOADK           2 3            ; z = 3

[4] NEWCLASS        3 K0           ; t = new <Point3D>

[5] NEWTABLE        4 0 3
[6] SETFIELD        4 "x" 0
[7] SETFIELD        4 "y" 1
[8] SETFIELD        4 "z" 2

[9] GETFIELD        5 K0 "__init"
[10] MOVE           6 3            ; self
[11] MOVE           7 4            ; args
[12] CALL           5 3 1          ; __init(self, args)


; --- Point3D.__init(self, args) ---

[1] GETFIELD        2 K1 "__init"  ; Point2D.__init
[2] MOVE            3 0            ; self
[3] MOVE            4 1            ; args
[4] CALL            2 3 1

[5] GETFIELD        2 1 "z"
[6] SETCLASSFIELD   0 2 2          ; self.z = args.z

[7] RETURN          0 1


; --- Point2D.__init(self, args) ---

[1] GETFIELD        2 1 "x"
[2] SETCLASSFIELD   0 0 2          ; self.x = args.x

[3] GETFIELD        2 1 "y"
[4] SETCLASSFIELD   0 1 2          ; self.y = args.y

[5] RETURN          0 1
Optimised bytecode where calling the parent requires a custom implimention
; --- call site: constructing with Point3D { x = 1, y = 2, z = 3 } ---

[1] LOADK           0 1            ; x = 1
[2] LOADK           1 2            ; y = 2
[3] LOADK           2 3            ; z = 3

[4] NEWCLASS        3 K0           ; t = new <Point3D>

[5] NEWTABLE        4 0 3
[6] SETFIELD        4 "x" 0
[7] SETFIELD        4 "y" 1
[8] SETFIELD        4 "z" 2

[9] GETFIELD        5 K0 "__init"
[10] MOVE           6 3            ; self
[11] MOVE           7 4            ; args
[12] CALL           5 3 1          ; __init(self, args)


; --- Point3D.__init(self, args) with Point2D default inlined ---

; inlined Point2D.__init (default)
[1] GETFIELD        2 1 "x"
[2] SETCLASSFIELD   0 0 2          ; self.x = args.x

[3] GETFIELD        2 1 "y"
[4] SETCLASSFIELD   0 1 2          ; self.y = args.y

; Point3D-specific field
[5] GETFIELD        2 1 "z"
[6] SETCLASSFIELD   0 2 2          ; self.z = args.z

[7] RETURN          0 1
Fully optimised bytecode where calling the parent is "default"
; --- fully optimised: no __init calls or table, fully inlined default chain ---

[1] LOADK           0 1            ; x = 1
[2] LOADK           1 2            ; y = 2
[3] LOADK           2 3            ; z = 3

[4] NEWCLASS        3 K0           ; t = new <Point3D>

[5] SETCLASSFIELD   3 0 0          ; self.x = x
[6] SETCLASSFIELD   3 1 1          ; self.y = y
[7] SETCLASSFIELD   3 2 2          ; self.z = z


If a class defines an `__init` function, it is understood to be a constructor. Classes with constructors follow different rules. We define class construction as follows:

Classes that have constructors cannot be constructed via `T()` syntax. Instead, the static method `.new` must be invoked. Classes that do not define constructors are still initialized via `T {...}` syntax.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oof. :(

class A extends B
public x: number

function __init(self, x, y)

@alexmccord alexmccord Jun 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so much like Python's __postinit. Then you'd only write the last line there, and the constructor call probably has to invoke this metamethod anyway. Then there's no need for T.new being sugar for __init (not to mention what happens if the function new exists along with __init).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in a dataclasses __post_init__? It feels much more akin to __init__ given a DC's __post_init__ only runs after initialisation, where you'd do (in Luau class terms) A { x = 1, ... }, rather than a custom A(some, arguments, here) constructor, then the post-init gets called on an already-populated object. This proposal seems to be focusing on self being entirely uninitialised at the start of __init, which would just be a normal __init__ on a python class.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, __post_init__ from Python's @dataclass, yeah. But re-reading this RFC, this specific class is better done with __post_init__ but the general problem is still not solvable with that one. Constructors vs new really does need to exist (kinda).

class A extends B
public x: number

function __init(self, x, y)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, __post_init__ from Python's @dataclass, yeah. But re-reading this RFC, this specific class is better done with __post_init__ but the general problem is still not solvable with that one. Constructors vs new really does need to exist (kinda).

public name: string

function new(): DerivedPoint
-- We're stuck! We cannot implement this function in terms of BasePoint.new()

@alexmccord alexmccord Jun 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so? DerivedPoint { x = 1, y = 2, name = "hello" }. The difference is that if the superclass has some field x and the subclass also has some field x, then that subclass must shadow that field x. This is only a problem if you need the side effects from BasePoint.new() or it has private fields. And in both cases, the problem stems entirely from inheritance in the first place.


Some developers will feel inconvenienced by the restrictions on uninitialized class fields. The current rules do not, for instance, permit a developer to write a helper function that partially (or completely) initializes a new class instance.

## Alternatives

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative (that I already liked, but am starting to really like it) is "Do not support inheritance" and now this entire category of problems are gone.

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.

8 participants