Skip to content

Draft: Alternative Class Syntax RFC#216

Draft
InfraredGodYT wants to merge 4 commits into
luau-lang:masterfrom
InfraredGodYT:patch-1
Draft

Draft: Alternative Class Syntax RFC#216
InfraredGodYT wants to merge 4 commits into
luau-lang:masterfrom
InfraredGodYT:patch-1

Conversation

@InfraredGodYT

Copy link
Copy Markdown

Rendered

This is an alternate draft of the existing Classes RFC intended to encourage discussion before native classes are implemented.

When the original RFC was accepted, I supported it because I was excited to see native classes come to Luau. After reading and participating in the subsequent constructor RFC discussion, however, I came to feel that constructors and encapsulation are fundamental enough to be designed alongside the core class syntax rather than introduced incrementally in later RFCs.

@InfraredGodYT InfraredGodYT marked this pull request as draft July 5, 2026 21:08
@Kampfkarren

Copy link
Copy Markdown
Contributor

Would help if you clearly documented what is different about this to the original

@InfraredGodYT

InfraredGodYT commented Jul 6, 2026

Copy link
Copy Markdown
Author

Would help if you clearly documented what is different about this to the original

The Compatibility section lists the differences from the original RFC. Would you like that section to go into more detail explaining why those differences exist?

Fixed typo of original RFC's table arg construction
@Bottersnike

Copy link
Copy Markdown

I don't think putting inheritance in "out of scope for now" really makes sense if the aim here is a one-shot of classes. It's something that is going to come up, and regardless of personal views on the correctness of using inheritance it's something people use a lot in their existing code that classes really ought to support if they are to replace use of metatable-based OOP.

As already evidenced, it's something that needs designed in from the get-go to ensure it works properly within classes, so I'd be really interested to at least see discussion about how it could work within this design.

@InfraredGodYT

Copy link
Copy Markdown
Author

I don't think putting inheritance in "out of scope for now" really makes sense if the aim here is a one-shot of classes. It's something that is going to come up, and regardless of personal views on the correctness of using inheritance it's something people use a lot in their existing code that classes really ought to support if they are to replace use of metatable-based OOP.

The aim of this RFC wasn’t a one-shot of classes or to say inheritance should never be in Luau, but to establish a stronger foundation for classes before tackling larger features. The goal here was to establish a more organized core object model that could still be amended in the future.

I left inheritance and composition out of scope because I didn’t have a design for them that I was confident in yet (which is also why I didn’t reserve protected and super yet). I was leaning more towards composition rather than inheritance, but I wanted more feedback before deciding if just one or both of them would be better to add.

@deviaze

deviaze commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

I don't like that this version of the RFC implicitly passes self and therefore disallows 'static' functions on a class.

static is a nonobvious term and would be a superficial keyword. A static function on a class should just be one that doesn't take in self, simplifying the mental model without adding another keyword.

@MagmaBurnsV

Copy link
Copy Markdown
Contributor

There seems to be a big tension in the design space here that I think needs addressing.

Obviously the current "table constructor" protocol is inspired by Rust's approach, where there is one constructor for a struct/class and all custom constructors are just static factory methods that wrap around the one true constructor.

There's some merit to this approach, as it guarantees initialization is atomic unlike typical OOP constructor setups. If you forget to initialize a field when constructing, then the typechecker will warn you about it, which is something not trivially doable with constructor inheritance. It also meshes nicely with a trait/interface system like the one Rust has.

However, the downside is that this doesn't compose at all with inheritance, since you can't just "extend" a static factory method. This is what the previous constructor RFC tried and failed to resolve. Atomic constructors are simply mutually exclusive to inheritance.

The irony is that people seem to really want inheritance, but to do so requires changing the constructor protocol to be nonatomic and unchecked (like this RFC proposes), which is unpopular. Likewise, we could keep the atomic constructor protocol, but then our only options for polymorphism are interfaces, which also seems unpopular.

@InfraredGodYT

InfraredGodYT commented Jul 6, 2026

Copy link
Copy Markdown
Author

I don't like that this version of the RFC implicitly passes self and therefore disallows 'static' functions on a class.

static is a nonobvious term and would be a superficial keyword. A static function on a class should just be one that doesn't take in self, simplifying the mental model without adding another keyword.

I didn't write out the section yet at the time of this reply, but implicit self would not apply to static functions or fields, which would use the static keyword. Static functions wouldn't be disallowed.

I plan to amend the RFC with a section dedicated to the semantics of static to clear up any confusion surrounding it.

@Bottersnike

Copy link
Copy Markdown

However, the downside is that this doesn't compose at all with inheritance, since you can't just "extend" a static factory method. This is what the previous constructor RFC tried and failed to resolve. Atomic constructors are simply mutually exclusive to inheritance.

I think the bigger problem with Bar({ foo = 1 }) isn't inheritance but rather just constructors in general. Factory functions are an option but aren't particularly ergonomic.

The Rust model works when you think about classes as just POD classes that have some methods on it but that's quite a specific model (akin to a C struct with some functions pointers on it) that doesn't particularly feel like "classes" in the ergonomic native way you'd expect from a high level construct.

@soyuta

soyuta commented Jul 7, 2026

Copy link
Copy Markdown

I like this RFC's implicit self, similar to how methods are currently declared with : in Luau, and I also like the introduction of private.

While using _camelCase for private members has been a long-standing convention, I think it's also reasonable for the language itself to enforce access restrictions, just like it does with const.

However, I don't like constructing classes by calling the class object itself like a function. It would break a long-standing convention in the Luau ecosystem, where native objects have traditionally used .create() while user-defined objects have used .new().

I'm also not sure about introducing a constructor keyword. It feels a bit unfamiliar in the context of Lua. Considering Lua's metamethod naming conventions, I think __init (as proposed in RFC #210) would be a better fit.

Or maybe it would be better to introduce a constructor modifier instead?

constructor function new()
end

constructor function create()
end

Kind of like public and private.

Constructors really seem to be the trickiest part of the class design.

@Bottersnike

Copy link
Copy Markdown

One problem .new() and .create() end up with is you're now reserving those method names to never be usable. They also don't match the function __init that was defined, which is Very Strange behaviour (see: unintuitive). Foo() to call the constructor means no names are reserved, matches what all other languages use for classes, and matches the "somewhat magic" nature of a constructor (be that via constructor() ... or function __init.

It's a pattern that's quite easily doable right now with metamethods by defining __call as a metamethod on the table that would otherwise have .new(), but it's not a frequently employed pattern because it's honestly a pain to write out all that code.

Comment thread docs/syntax-classes.md Outdated
Comment thread docs/syntax-classes.md Outdated
Comment thread docs/syntax-classes.md Outdated
Comment thread docs/syntax-classes.md
Comment thread docs/syntax-classes.md Outdated
Comment thread docs/syntax-classes.md
If instance methods are going to have an implicit self parameter than introducing `static` to have class-specific members instead of instance-specific members becomes necessary.
…ctor

This change was amended to the RFC due to the feedback I received on the Roblox OSS Discord. This change makes the RFC more viable for POD instances like the original.
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.

7 participants