Skip to content

class: named constructor .new instead of callable class - #622

Closed
lneto wants to merge 1 commit into
masterfrom
claude_class_new
Closed

class: named constructor .new instead of callable class#622
lneto wants to merge 1 commit into
masterfrom
claude_class_new

Conversation

@lneto

@lneto lneto commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

The OOP-style modules (socket.inet/unix, netlink.rt/genl) were callable classes: require returned a class you invoked to construct, e.g. require("netlink.rt")() / inet.tcp(). Two problems:

  • Inconsistent with the rest of lunatik, where you require a table and call a named constructor (socket.new, rcu.table, data.new). The callable form reads as an anonymous call, and calling a method on the class by mistake fails cryptically (on an internal sequence field) instead of clearly.
  • __call only works one subclass deep. A grandchild class (e.g. an nl80211 built on genl) is not callable, because __call is looked up on the metatable by rawget and is not inherited. That forced a hand-rolled setmetatable in the grandchild.

New shape

class{} returns a table; :extend{} derives a specialization; .new(...) builds an instance, running the class's :init(...); close is wired as the to-be-closed handler. Constructors move from :__call to :init.

local rt = require("netlink.rt")
local session = rt.new()          -- was rt()
local s = inet.tcp.new()          -- was inet.tcp()
local u = unix.stream.new(path)   -- was unix.stream(path)

No __call, so subclassing works at any depth — this dissolves the grandchild problem (the nl80211 setmetatable hack goes away when that branch adopts this).

Validated in the kernel (without a module reload): rt.new()/genl.new() drive the netlink dumps, and unix.stream.new(path)/inet.tcp.new() construct working sockets. The full socket and netlink suites should be run on merge.

Open branches that construct these (nl80211 #617, netfailover #618, floodguard #619) rebase onto this and adopt .new.

🤖 Generated with Claude Code

The OOP-style modules were callable classes: require returned a class you invoked
to construct (rt(), inet.tcp()). That is inconsistent with the rest of lunatik,
where you require a table and call a named constructor (socket.new, rcu.table,
data.new), and it makes construction read as an anonymous call. And the __call
constructor only worked one subclass deep: a grandchild class (an nl80211 built
on genl) was not callable, since __call is not inherited as a metamethod.

Now class{} returns a table, :extend{} derives a subclass, .new(...) builds an
instance running the class's :init(...), and close is the to-be-closed handler.
Constructors move from :__call to :init; callers construct with rt.new(),
genl.new(), inet.tcp.new(), unix.stream.new(path). No __call, so subclassing
works at any depth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant