Add types to the source code #61
Labels
design
Concerns the architecture of the engine
enhancement
New feature or request
refactoring
This requires refactoring existing code
standardization
Concerns conventions that should be strongly followed in mods
undecided
Issues for which there are multiple potential solutions/none decided yet.
I don't particularly regret choosing Lua as the language for the engine (being as it has only a single user as of present writing), but I do wish there were at least type hints for the many data structures like map objects or the bundles of parameters passed to callbacks or events. It's difficult to figure out what the contents of a
params
table is without searching the source code or manually adding documentation and constantly making sure it doesn't go out of sync. The problem is that I've been spoiled by the program introspection features that are central to my workflow and can't seem to give up the things that Lua offers, even though it's a dynamically typed language. Basically, what I want is a language that:As far as I'm aware, this language does not exist yet. The crucial point here is that, unlike most dynamic languages like JavaScript or TypeScript, the import mechanism in Lua is a function instead of a statement. This means you can call
require
as a function to get back a dictionary containing a complete module, and furthermore that you could replacerequire
with your own implementation that does some custom module bookkeeping, as OpenNefia does. I don't think you can do this in ES6, TypeScript or Python, where you can't hot-reload modules at runtime in the same way, or sometimes at all, because the module system is so tightly integrated with the language and its ecosystem that you can't hook into the way modules are produced or loaded. From the Python docs forimportlib.reload()
(emphasis mine):We avoid this in OpenNefia by enforcing the convention of returning a complete module table from each Lua file, and using that returned table to do late binding. (
Rand.rnd(42)
overlocal rnd = Rand.rnd; rnd(42)
) This way you can reload just the single module's file/namespace and have all other references to the module in the running program updated automatically, so long as late binding is being used (no upvalues). Perhaps this is why Lua is the only easily recognizable, "mainstream" programming language that shows up in the list of notable live coding environments on Wikipedia, alongside the likes of more obscure fare like Max and Sonic Pi.But it seems that none of the general-purpose programming languages with this kind of module system have a type checker. Which is a bummer, because that means you have to choose whether you want program introspection and hotloading or the ability to catch simple type errors at compile time instead of runtime.
The good news is that seems like Teal might finally become the solution at some point. With Teal, you can essentially write "TypeScript, but Lua," catch type errors before running any code, and then compile the source to Lua and load it just as before. Since the compiled code is just Lua, you'd still be able to take advantage of hotloading where it was previously possible, so long as the types work out. And you could just ignore the typechecker if you know what you're doing, as Teal is merely a small addition of syntax to standard Lua.
As far as I know, Teal is the first programming language to both use a type system and allow for runtime module hotloading.
In regard to OpenNefia, I don't know what porting the codebase to Teal would look like, or if porting the entire codebase instead of small pieces would be feasable. As the time of writing it isn't possible at the moment to practically adopt Teal with the amount of metatable and class magic being used, since Teal currently doesn't have any way of specifying functions that create new types in a metaprogramming fashion. But I think having types would make it way, way easier to mod things when you see a function argument or table and no longer have to worry about searching through the entire codebase or putting down a breakpoint in-game in order to figure out what fields it contains.
A couple of questions I keep coming back to when it comes to adding types:
ldoc
for Teal at that point. But even having types alone would stand as a decent source of documentation in itself, if the alternative is guessing.table.replace()
on a table returned from Teal'srecord
statement for all types detected in a module's table and ignoring any errors for code that's already loaded.The text was updated successfully, but these errors were encountered: