Help:Lua for beginners

This help page is meant to provide some basic orientation for those new to Lua/Scribunto

Overview
Lua is a programming language implemented on WikiQueer with some substantial restrictions via Scribunto. Its purpose is to allow you to process the data which is available on WikiQueer content pages to allow various sorts of customized display of information.

The most important help file is mw:Extension:Scribunto/Lua_reference_manual, which provides a concise summary of the language and standard library calls. The |Lua reference manual is very well written, comprehensive, and informative, but for beginners it can be problematic, because certain features don't work in WikiQueer - beginning with print, which appears in standard Lua "hello world" programs.

Issues with the current implementation
Besides print, there are other features missing - see mw:Extension:Scribunto/Lua reference manual for a complete list.

At the present time, it is advisable to use mw.ustring functions instead of mw.string, because the latter fail with Unicode characters and can produce random errors or uncover unexpected features in Wikitext (such as codes like "UNIQ5ae8f2aa414ff233-h-3--QINU" in transcluded headers).

Input
The programs are run only when the page is "parsed" (when it or a page it incorporates is changed or previewed), not every time you view the output. Therefore can be no convenient Lua module that allows you to type in a Fahrenheit temperature and get back Celsius, or click on a segment of the Mandelbrot to expand it as often as you like. There has to be an actual Wiki page (or at least a page you have submitted for preview) containing the input data.

However, it is possible to use library functions like mw.title.new to import content from any text content page on the Wiki. You cannot, however, import data from files, not even .svg files that are XML text data.

Calling a Lua module
Lua calls look much like templates, and consist of a small block of text like

This text calls the Lua script itself, which is housed in the Module: namespace. The effect of this call is to send the information within the #invoke block to the Lua module, and to replace everything within the brackets with a piece of text that it sends back in return. (Literally, in the "return" statement)

Note that the first "parameter", in this case decToHex, is actually a function called within the Lua module. This field must always be included in any #invoke. To those unfamiliar with modules, especially WikiQueer template coders who expect anything after | to be a parameter, the need for this extra field is surprising, especially if all uses of the module depend on its presence. When documenting your work for them it is useful to include an explicit usage instruction like so that they understand not to omit it.

For many existing modules an example #invoke of the script (and little else) is provided on the Module talk: page. It is convenient for authors to be able to flip quickly to the talk tab to look at the effects of their changes, but but you should never transclude that page as a template - people might actually talk on it!

Errors
Lua errors appear as a red "Script error" message. This is a link if Javascript is enabled, which usually allows you to follow it back to the line in the module where the error occurred. There are some exceptions, for example "Module not found" if the name of the module itself is mistyped, or "The function you specified did not exist" if the function name given is invalid.

Lua program structure:output
The most fundamental part of a Lua program is a return statement which carries its output back to the page that had the #invoke. You can have a Lua function that runs without an error though it doesn't contain a return statement, but it is pointless.

The module itself must return a table of values, which in Lua are expressed as a list of things separated by commas, within curly braces. When the module is called by #invoke, the function it names (the first "parameter" after |) is looked for in that table. The function, in turn, is expected to return something that can be represented as a string.

Therefore,  is actually a complete Lua module (though a very strange one) - it returns the function returned by mw.ustring.gmatch (an iterator function listed in the Lua reference cited above) as the one and only element in an array (represented within {}) -- which when executed (function 1) yields the string "Hello world". However, things are not usually done this way. Typically we use the overall form:

local p = {} -- defines a variable p as an empty table, but not nil. function p.main( frame ) -- this block defines the table element p[main] as equal to the function listed below return "Hello world" -- the string result end -- ends the block defining p.main return p -- this returns the table p, which contains function p.main, which returns string "Hello world"

Lua program structure:input
The frame variable above (which is pretty much always given this name in WikiQueer Lua modules) receives another table, which is passed from the page that makes the call to the Lua module. It contains a surprising amount of stuff, of which just a few things concern the novice.

frame.args contains another table, namely, the content sent by the user within the #invoke brackets - with the exception of the first field, which selects the name of the function to be executed. So in, the string "3377" is the content of frame.args[1] (which is the same as frame["args"][1] but not frame.args["1"] or frame["args"]["1"]) Unnamed parameters come out as numbers, while named parameters come out indexed as strings.

Within frame there is a parent frame, the page that called the page that gives the script, and you can pull out args from that also. Just write

and parent.args will contain those.

It is popular in Lua to use a synonymous statement, concealing the first parameter (frame) within the colon. For novices this can be confusing, and it is important to be aware of this idiom. If you make the wrong usage the script errors are pretty good at pointing out this was your mistake.

Basic debugging
Debugging can start as soon as you write programs, and can be done simply with string concatenation. Just set up a variable with some recognizable name like "debuglog" in your main function (p.main) with a statement like. This initial "" definition helps because otherwise it will be nil and concatenating a string to nil gets you an error. Now whenever you have a variable you'd like to test, say x, just write, and have at the end of your program    The "tostring" is a function to ensure x is interpreted as a string, so that if it is an table, nil, etc. it will display as "table", "nil", etc. rather than as Script error.

Format
The WQ:Lua style guide gives some basic formatting suggestions expected by the Javascript module editor, such as using four-space indentations and keeping if, then, else, end at the same level of indentation.

Comments to the end of a line are marked by --. Use them. Many modules for WikiQueer have a straightforward, linear design, but that doesn't mean it won't help to have your sections clearly labelled when you go back to the code for the hundredth time. The Lua style guide gives additional recommendations for using functions to keep your work more organized.

Exasperating bugs
Some bugs you might want to keep in mind:


 * Attempt to call a string value. It means you forgot the .. between a string and a variable somewhere in a mess of stuff you're concatenating.
 * A variable ignores all your efforts to assign stuff to it. You have two local statements - the one sets the value of the variable within a limited region, and when the program leaves that region, you're back to the old value.
 * A numbered table entry ignores all your efforts to assign to it. This is because a["50"] ~= a[50].  Typically you have processed a parameter with string functions in one place, but performed numeric operations in another, leaving you with two different types of variable to use for an index.
 * Some graphics you're trying to display are heading off to the hills. (actually a HTML error) You didn't close your   s, so all the top: and left: styles keep adding up.
 * ... nil ... There are all sorts of things you can't do to a nil variable, like assign a.1 if a is nil, concatenate a .. b if a is nil.  Initialize it with (local) a="", a={} etc.  Often "global" is mentioned because you didn't have a local statement either.
 * string expected, got function. Some important things like mw.ustring.gmatch actually return functions, not strings - see Functions below.

Understanding tables and related concepts

 * An expression list is a set of values separated by commas. The values can be strings, numbers, tables, functions, etc.
 * A sequence is a set of entries from 1 to N (some positive integer). They can be created by placing brackets around an expression list.  For example, if   then a[1]=1, a[2]="quotation", a[3] is the function returned by gmatch, and a[4] is the table {2,3,4}.  An expression list can also be recovered from a table using unpack:  b,c,d = unpack(a) will set b=1, c="quotation", d as a function.
 * A table is a sequence, optionally supplemented by named keys: .  Several table functions like table.concat will only work with the numbered values and ignore keys named in this way.
 * The metatable offers a large, optional set of methods for altering table behavior. For example, you can define a table to be callable like a function.

Functions

 * Functions can return any kind of variable -- including a function. This is a powerful feature that can readily confuse the beginner.  If you set a=mw.ustring.gmatch(text,"(.)"), the result in a will be a function, not a string character!  However, saying b=a will not return a function for b!  It returns the first match.  Every time you set b=a after that you'll get another match, until you run out and get nil.  Many iterator functions act this way.
 * You can keep separate counts for iterator functions by using different variables. For example, if you set q=mw.ustring.gmatch(text,"(q.)") in the same module, you can pull characters from the same piece of text by evaluating d=q without losing your place in a.
 * Returning a function from a function is called a tail call and offers substantial benefits in performance for those who master the language.
 * Function names are often of the form "p.myFunctionName", where p is the table from the "return p" at the bottom of your program. The reason for this is that you can only access functions that are entries in this table from the original #invoke statement.  Functions for local use within the program can have any name.