Skip to content

Instantly share code, notes, and snippets.

@Anaminus
Last active September 29, 2020 01:39
Show Gist options
  • Select an option

  • Save Anaminus/2c161f9e4b8145b3efdb3ffa3c360c4d to your computer and use it in GitHub Desktop.

Select an option

Save Anaminus/2c161f9e4b8145b3efdb3ffa3c360c4d to your computer and use it in GitHub Desktop.
Lightweight interfaces.
local Impl
local Is
local Interface
do
local implementedInterfaces = setmetatable({}, {__mode="k"})
local embeddedInterfaces = {}
local nillableInterfaces = {}
-- Impl declares a value to implement zero or more interfaces, which must be
-- strings. A value can be declared only once, and subsequent attempts do
-- nothing. Impl returns the value.
--
-- If an interface string has been declared previously with Interface, then
-- the value will automatically implement any embedded interfaces.
--
-- When Impl is called with a value and no interfaces, the value is said to
-- "implement nothing". A value that has not been called with Impl is said
-- to "not implement".
function Impl(value, ...)
if implementedInterfaces[value] then
return value
end
if value == nil then
return nil
end
local interfaces = {}
implementedInterfaces[value] = interfaces
local args = {...}
for i = 1, #args do
local interface = args[i]
if type(interface) == "string" then
interfaces[interface] = true
local embedded = embeddedInterfaces[interface]
if embedded then
for embed in pairs(embedded) do
interfaces[embed] = true
end
end
end
end
return value
end
-- Is returns whether value v implements interface I. If v is nil, then Is
-- returns true if the interface is nullable. If I is nil, then Is returns
-- whether v implements.
function Is(v, I)
local interfaces = implementedInterfaces[v]
if interfaces then
if I == nil or interfaces[I] then
return true
end
end
if v == nil and nillableInterfaces[I] then
return true
end
-- TODO: If v is a string, check if it is an interface that embeds I.
return false
end
local declaredInterfaces = {}
-- Interface declares and describes an interface. The first argument is the
-- interface string, and each remaining argument is a string describing a
-- behavior of the interface. An interface can be declared only once.
--
-- If a behavior string matches an interface string declared previously,
-- then that previous interface is embedded into the current interface.
--
-- If a behavior is a nil value rather than a string, then this makes the
-- interface "nullable", effectively causing nil to implement the interface.
-- This behavior is inherited from embedded interfaces.
function Interface(interface, ...)
if declaredInterfaces[interface] then
return
end
if type(interface) ~= "string" then
return
end
local embedded = {}
local behaviors = {}
local notnull = true
local args = {...}
for i = 1, select("#", ...) do
local behavior = args[i]
if type(behavior) == "string" then
local subEmbedded = embeddedInterfaces[behavior]
if subEmbedded then
embedded[behavior] = true
for embed in pairs(subEmbedded) do
embedded[embed] = true
end
if nillableInterfaces[behavior] and notnull then
notnull = false
nillableInterfaces[interface] = true
end
behaviors[#behaviors+1] = behavior .. " <embedded>"
else
behaviors[#behaviors+1] = behavior
end
elseif behavior == nil and notnull then
notnull = false
nillableInterfaces[interface] = true
behaviors[#behaviors+1] = "<nullable>"
end
end
declaredInterfaces[interface] = behaviors
embeddedInterfaces[interface] = embedded
return
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment