Estrita

Estrita (Portugese for Strict) is a satirical superset of Lua 5.4, and a direct parody of TypeScript. The language enforces extremely strict typing with no room for error, in direct contrast to Lua's otherwise lenient and flexible structure.
Features not found in traditional Lua try to follow the syntactic language of Lua.
__FORCETOC__
== Key Features ==
=== Strict Typing ===
All variables are required to be declared with a type annotation, following the syntax of local varName: type = value. All valid types include those outputted by type()The full list is: boolean, number, string, function, userdata and thread., with the exception of table and nil. The usage of union types, custom types, and nil are not allowed, however you may imply that the variable will be defined later with the use of a question mark.
Tables and functions have special syntax for type annotations, shown in the later sections.
local hello: string = "Hello, World!"
local pi: number = 3.141
local setting: boolean = false
local a = 5 -- Invalid, implicitly 'any'
local b -- Invalid, implicitly 'any or nil'
local c: number -- Invalid, type doesn't match value
local c: number? -- Valid
Constant and to-be-closed variables work as normal.
local mode <const>: number = 5
local file <close>: userdata = io.open("./file.txt", "r")
Types are immutable and must be adhered to throughout the variable's lifecycle.
local foo: number = 5
foo = 7
foo = 9
foo = "10" -- Invalid
local bar: number = 10
bar = 11
bar = nil -- Invalid
local baz: number? = 12
baz = 15
baz = 21
baz = nil -- Valid
baz = "21" -- Invalid, 'baz' is still defined within the scope
==== No Globals ====
Global variables are not allowed. If you require such variable, you must do so by directly referencing the _G table, alongside following the schema. A later chapter will go over this in more detail, alongside changes to _G itself.
=== Table Schemas ===
All tables are required to be declared with a schema. A schema is declared in a syntactically similar way to that of regular tables, with the left side used for a key, and the right side used for the type itself. The ... operator may also be used here for variable length schemas. If you need more flexible definitions, you can use square brackets alongside a type in the middle for the key.
Note that while empty schemas are technically valid, there would be no way to use the table with the schema as all new entries to the table make the table invalid, thus empty schemas are considered to be illegal.
local account = {name = "John", balance = 100} -- Invalid, missing a schema
local account: table = {name = "John", balance = 100} -- Invalid, 'table' isn't a valid type annotation
local account: {name = string, balance = number} = {name = "John", balance = 100} -- Valid
account.name = "Adam"
account.balance = 200
account.id = 50 -- Invalid, table no longer follows schema
account.name = nil -- Also invalid
local array: {[number] = number} = {1, 2, 3}
table.insert(array, #array, 4)
table.insert(array, #array, 5)
array[10] = 10 -- Note that gaps are allowed in this case
local arrayStrict: {[1] = number, ...} = {1, 2, 3} -- '...' inherits the type annotation of whatever was previous of it
arrayStrict[4] = 4
arrayStrict[6] = 6 -- No longer follows the schema since there is a 'nil' value where a 'number' should go
Schemas can be referenced by mentioning the variable with the schema, allowing reuse and containment. Schemas can also be extended with the and keyword.
local account: {name = string, balance = number} = {name = "Account", balance = 0}
local john: account = {name = "John", balance = 100}
local admin: account and {permission = number} = {name = "Admin", balance = 100000, permission = 10}
local list: {[string] = account?} = {John = john, Admin = admin}
Adopting the keyword from functions, you can use self or this within schemas and tables to refer to the topmost table.
local foo: {foo = self} = {self}
print(tostring(foo)) --> table: 0x________
print(tostring(foo.foo)) --> table: 0x________
print(tostring(foo.foo.foo)) --> table: 0x________
 -- same table addresses
=== Function Annotations ===
Functions are defined as usual, with the function keyword. All inputs and outputs require type declarations. Unlike variables and tables, functions may allow for nil to be used as an output. A semicolon is required after output type annotations. An additional never type is provided for functions which will never halt (and this WILL be checked), but only if the Halting problem has been successfully solved and implemented.
function foo(a: number, b: number): number;
 return a + b
end
function bar(message: string): nil;
 io.write(message)
end
function baz(): never;
 while true do end
end
Functions allow for method overloads. This is the same as defining the value holding the function repeatedly with new arguments and outputs.
function leftpad(str: string, width: number, padding: string?): string;
 padding = padding or " "
 return padding:rep(width - #str) .. str
end
function leftpad(num: number, width: number): string;
 return string.format("%0"..width.."d", num)
end
print(leftpad("hey", 5)) --> " hey"
print(leftpad(102, 5)) --> "00102"
self and this will implicitly use the schema of the containing table, but only if they are defined as the first argument of the function. This structure will also enforce all calls to the function use the appropriate syntactic sugar.
== _G Changes ==
_G now follows the schema of: {[string] = {[string] = function}, print = function, (and so on).
== Metaprogramming ==
Metatables are separate from regular tables, and do not count towards schema checks. They do however have their own schemas. You would write metatables as you usually would.
local object: {[string]: string} = {}
local Object: {mt: {[string] = function, __index = self}, new = function} = {
 mt = {
 __call = function(): nil;
 print("Hello, World!")
 end,
 __index = self
 },
 new = function(self): object;
 local new: object = {}
 setmetatable(new, self.mt)
 return new
 end
}
local thing = Object.new()
thing() --> Hello, World!
== Notes ==
Category:2024 Category:Turing complete Category:High-level Category:Unimplemented