• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] Teal - typed dialect of Lua

Status
Not open for further replies.
Level 13
Joined
Nov 7, 2014
Messages
571
Teal is a typed dialect of Lua. It allows one to annotate Lua declarations with types that are then checked by the 'tl' compiler.

The attached Teal script converts Jass declarations to Teal declarations. You probably want to run it on your 'common.j' / 'blizzard.j' files, like so:
Code:
tl run gen-tds-from-jass.tl path/to/file.j > output-file.d.tl    -- if you don't redirect, it writes to stdout

It generates declarations like these (common.j was used as input):
Code:
global agent = record end
...
global ConvertRace: function(i: number): race
...
global FALSE <const>: boolean
...
global CreateUnit: function(id: player, unitid: number, x: number, y: number, face: number): unit

The type declarations are only necessary to make the compiler happy, they don't generate any Lua code. In order for the 'tl' compiler to find these declarations you need to create a 'tlconfig.lua' file:
Code:
return {
    include = {
        'directory/with/teal-type-declarations/',
    },
    preload_modules = {
        'commonj', -- real file name is 'commonj.d.tl'
    }
}


Running the 'tl' compiler without installing LuaRocks

You need to download the following files:
Code:
tl - from the Teal repository
tl.lua - from the Teal repository
argparse.lua - from https://github.com/mpeterv/argparse -- dependency for 'tl'

Then you can run 'tl' like so:
Code:
lua tl -h

You need a Lua interpreter somewhere in your path though.
 

Attachments

  • gen-tds-from-jass.tl.txt
    4.6 KB · Views: 54
  • Like
Reactions: ~El
Level 12
Joined
Jan 30, 2020
Messages
875
Hello there !

Sorry, I might not yet have enough knowledge to clearly see what this really does.
Seeing the words "typed" and "Lua" in the same sentence triggered my interest though.

Could you make a more "noob"-friendly version of this post, indicating what this can help for a bit more clearly ?

Example : from a noob point of view, your first sentence here makes no sense :
It allows one to annotate Lua declarations with types that are then checked by the 'tl' compiler.

If I try to understand that, it says "Teal allows to add types declarations that are checked by Teal compiler". Errr wait what ?

The idea or processing the common.j, blizzard.j (nooo stay away frommm thisss) and probably common.ai makes sense though.
The rest is quite understandable, happy I learn that there was a Lua package manager called LuaRocks (can it be of any use in the wc3 context?)

Now what does this mean/imply :

You need a Lua interpreter somewhere in your path though.
??

Sorry if I sound a bit too noobish, but I am clearly interested but definitely not experienced enough to figure this out.
 
Level 13
Joined
Nov 7, 2014
Messages
571
Suppose we wanted to write the function IsUnitGroupDead which returns true if all units in the group are dead, otherwise it returns false.

In plain Lua it might look like this:
Lua:
function IsUnitGroupDead(g)
    for idx = 1, BlzGroupGetSize(g) do
        local u = BlzGroupUnitAt(g, idx)
        if UnitAlive(u) then return false end
    end
    return true
end

In Teal it might look like this:
Lua:
function IsUnitGroupDead(g: group): boolean
    for idx = 1, BlzGroupGetSize(g) do
        local u: unit = BlzGroupUnitAt(g, idx)
        if UnitAlive(u) then return false end
    end
    return true
end

We've simply annotated with types the function 'IsUnitGroupDead' (we've added the type of the 'g' argument, and the type of the result of the function).

The Teal compiler can now help us by catching mistakes when calling this function. But it needs to know what 'unit', 'group', 'BlzGroupGetSize', 'BlzGroupUnitAt' and 'UnitAlive' are first. This is where the generated declarations from 'common.j' come in handy.

Now what does this mean/imply :

You need a Lua interpreter somewhere in your path though.

In order to run the 'tl' command/script, one needs a Lua interpreter to do so (the 'tl' file is a plain Lua file). By Lua interpreter, I mean 'lua.exe' (on windows). One can build 'lua.exe' from source or download a pre-build binary, but regardless of where we got it from, in order to execute the following command:
Code:
lua.exe tl -h

The command interpreter 'cmd.exe' needs to know where to look for 'lua.exe'. We can tell it where to look for 'lua.exe' by modifying our 'Path' environment variable or use an absolute path:
Code:
path/to/lua.exe tl -h
 
Level 12
Joined
Jan 30, 2020
Messages
875
Ok this makes much more sense now, thanks.

In all fairness this is a step forward, although I very often tend to make syntax errors as my vision has lost some accuracy over the years.
Maybe I should dig more into Luacheck that you posted about a while back when I was converting from Jass, but I seem to remember that it did not take all new variables or function declared it the checked script itself, throwing "undefined variable" or "non-standard global" at nearly every line.

What limits this Teal tool in the wc3 Lua context is probably the fact that you usually know (by heart) what type of values you pass to functions in your own code.
Doesn't mean it is not useful, but I tend to believe what makes people get away from Lua is rather the complete absence of syntax error as the language allows you to use any kind of variable at anytime in the script, weather it has been declared or not.

I don't k,now if there is something that can perfectly compensate from this, but that would really be helpful if there was.
 
Level 12
Joined
Jan 30, 2020
Messages
875
I suppose to save hours trying to pinpoint where you messed up a single letter in a variable name somewhere as Lua simply doesn't care ;)

To be honest, although I love some aspects of Lua, including the way it handles tables, I still feel very insecure with it as any stupid typo in the code can be fatal.

EDIT : Yes I know I should drop Notepad++ for VSCode, but it will take some time for me to get really comfortable with it.
 
Level 13
Joined
Nov 7, 2014
Messages
571
Why would you use lua and then reintroduce rigid types?
The type checker catches mistakes, which saves time. Type annotations act as a a kind of documentation, they help the type checker and help you to understand the "logic". These allow you to make your map more complex and make it easier to change/improve.

Doesnt that defeat the point of not using a type-safe language?
Lua is a type safe language. Lua's values have types (integer, float/double, string, table, function, etc.), but Lua variables don't. Sure, adding type annotations can be a bit annoying at the start, but their benefits far outweigh their inconvenience. There's also type inference, which allow you to omit the type annotations when they are obvious (local s = 'my string', etc.).
 
Geez, I know the advantages of having type-safety on variables. I was just pulling a joke on the crowd who praises LUA implementation as the second coming of christ when it does have its shortcomings.

I personally can't bring myself to like languages that do not require type declaration for variables. This is why I simply can't make myself like Python either, even though it does make some stuff ridicolously easy.

To be honest, although I love some aspects of Lua, including the way it handles tables, I still feel very insecure with it as any stupid typo in the code can be fatal.
There is nothing wrong with that opinion. You're not alone on that front.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
I personally can't bring myself to like languages that do not require type declaration for variables.

I like optional typing. Types help to make proper contracts, so in public functions, the parameters should of course by typed. But on a lower level, that might be too rigid like when you have transformations of some data. And then you often want to transitively apply the type of some other declaration.

edit: And with Linter rules you can still enforce types in certain places.
 
Last edited:
Status
Not open for further replies.
Top