Diary 2019-07-25

By Max Woerner Chase

I guess I didn't feel strongly enough about working on stuff related to Seed, Pijul, Poetry, or pip, so I... didn't.

Instead, I've now got this Lua class stuff in... a state. The current big deficiency is documentation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
---
-- A small class system.
-- @module class

-- Signature documentation.
do -- luacheck: ignore 541

    --- Signatures.
    -- @section signatures

    --- Module table.
    -- @field module

    --- Metatable.
    -- @table metatable
    -- @field __new constructor

    --- End Signatures
    -- @section end
end

-- Module export

--- Create a class; provides sugar of the form class[M].name(meta).
-- @function class
-- @tparam module|nil M
-- @tparam string name
-- @tparam[opt] metatable meta
-- @treturn table
local class

local function new_instance(metatable, ...)
    return setmetatable(metatable.__new(...), metatable)
end

local function empty_table() return {} end

local function new_class(mod, name, metatable)
    metatable = metatable or {}
    metatable.__name = name
    metatable.__new = metatable.__new or empty_table
    if mod then
        mod[name] = metatable
    end
    return metatable
end

local function basic_metaclass()
    return {
        __call = new_instance,
        __new = new_class,
    }
end

local class_class = basic_metaclass()

class = new_instance(class_class, nil, "class", basic_metaclass())
class(nil, "class_class", class_class)

local class_in_module = class(nil, "class_in_module", {
    __new = function(cls, mod)
        return {cls, mod}
    end,
    __index = function(self, name)
        if type(name) ~= "string" then
            return
        end
        local cls, mod = table.unpack(self)
        return function(metatable)
            return cls(mod, name, metatable)
        end
    end,
})

local mod_types = {["nil"] = true, table = true}

function class_class.__index(self, mod)
    if not mod_types[type(mod)] then
        return
    end
    return class_in_module(self, mod)
end

return class

The basic idea of this stuff is "lots of things are objects, including things that probably don't need to be". The intended usage of this stuff is that consumers local class = require("class") (or local class = require"class"; the interaction between Lua's function calls and literals is... interesting), create a module table like local M = {}, and then use the syntactic sugar class[M].MyClass{__index = {value = "whatever"}, __new = function(value) return {value = value} end}, which in this example would create a class in M called MyClass, which defines a constructor (the __new function, which I'm kind of just hoping nothing else that uses metatables collides with) that takes an optional argument and stores it in the value key; if nothing is passed, accessing value will return the string "whatever" per normal metatable mechanics. In the course of normal usage, the class constructor expects the last argument passed, if truthy, to be a table literal; the need to pass a variable in the course of defining the system is unfortunate, but unavoidable given the semantics in question.

One thing that all classes, including the "built-in" ones from this module will need to document, is whether their constructor returns a completely new object, or something passed in via its arguments. I think the current convention for class creation showcases the furthest limits of argument mutation and returning:

This has been fun, but to make use of this, I need a project to go with it, and ideally to have Seed working. The current hurdle with Seed is that I need to write code to extract the author config; if present, make "author" flags optional, otherwise required. It's kind of fiddly, oh well. I've got time, I'll try to get that done quick. Okay, I'm generating the paths, probably. From there, it shouldn't be hard to extract the author data, if it's present, and create a placeholder if it's not.

One bit of weirdness: the first record showed two diffs, and I recorded the first; the second record also showed two diffs, the second diffs together made up the first second diff. I should probably ask about that.

Okay, definitely need to wrap up. Good night.