git @ Cat's Eye Technologies Dipple / master lua / vrecord.lua
master

Tree @master (Download .tar.gz)

vrecord.lua @masterraw · history · blame

--
-- Simple Variant Records in Lua.  Basically equivalent to algebraic data types.
-- In this version, they're not opaque.  For best results, do not touch innards.
--
-- SPDX-FileCopyrightText: Chris Pressey, the original author of this work, has dedicated it to the public domain.
-- For more information, please refer to <https://unlicense.org/>
-- SPDX-License-Identifier: Unlicense
--

--
-- config is a table mapping tags to arrays of field names.
-- (arrays in Lua are number-keyed tables.)
--
function make_vrecord(config)
    return {
        maker = function(tag)
            local fields = config[tag]
            if not fields then
                error("Undefined tag: " .. tag)
            end
            return function(...)
                local num_args = select("#", ...)
                if num_args ~= #fields then
                    error(
                        "Arity error: expected " .. tostring(#fields) ..
                        " for " .. tag .. ", got " .. tostring(num_args)
                    )
                end
                return {
                    tag = tag,
                    field_values = {...}
                }
            end
        end,
        case = function(obj, cases)
            local tag = obj.tag
            local field_values = obj.field_values
            local fields = config[tag]
            if not fields then
                error("Undefined tag: " .. tostring(tag))
            end
            if #fields ~= #field_values then
                error("Arity error")
            end

            local sel = cases[tag] or cases.otherwise
            if not sel then
                error("No case for tag: " .. tag)
            end

            return sel(table.unpack(obj.field_values))
        end
    }
end

--
-- Example
--

demo1 = function()
    local List = make_vrecord({
        null = {},
        cons = {"head", "tail"}
    })

    local cons = List.maker("cons")
    local null = List.maker("null")()

    local dump
    dump = function(list)
        List.case(list, {
            cons = function(head, tail)
                print(head)
                dump(tail)
            end,
            null = function()
            end,
            otherwise = function(ooo)
                print("Not a list: " .. tostring(oo))
            end
        })
    end

    local abcs = cons("a", cons("b", cons("c", null)))
    dump(abcs)
end

--demo1()