Inheritance via Fallbacks
One of the most interesting uses of fallbacks is in implementing inheritance in Lua. Simple inheritance allows an object to look for the value of an absent field in another object called its "parent;" in particular, this field can be a method. This mechanism is a kind of object inheritance, in contrast to the more traditional class inheritance adopted in Smalltalk and C++.
One way to implement simple inheritance in Lua is to store the parent object in a
parent for instance, and set an "index" fallback function;
see Listing Five. This code defines a function
herit and sets it as the index fallback. Whenever Lua attempts to
access a field that is absent in an object, the fallback mechanism calls the
Inherit. This function first checks whether the object has a
parent containing a table value. If so, it attempts to access the
desired field in the parent object. If the field is not present in the parent,
the fallback is automatically called again. This process is repeated "upwards"
until a value for the field is found or the parent chain ends. When better
performance is needed, the same inheritance scheme can be implemented in C using
Being an interpreted language, Lua provides some reflexive facilities. One
example is the function
type, already mentioned. Other powerful reflexive
next, which traverses a table, and
traverses all global variables. The function
next gets two arguments, a
table and an index in the table, and returns a "next" index in some
implementation-dependent order. (Recall that tables are implemented as hash
tables.) It also returns the value associated with the index in the table.
(Recall that functions in Lua can return multiple values.) The function
nextvar has similar behavior, but it traverses the global variables
instead of the indices of a table.
An interesting example using reflexivity is dynamic typing. As noted before, Lua
has no static typing. However, sometimes it is useful to check if a given value
has the correct type to prevent weird behavior in a program. It's easy to check
simple types with
type. But for tables, we must check whether all fields
are present and correctly filled.
Using Lua's data-description facilities, you can use values to describe types: A single type is described by its name, and a table type is described by a table that maps each field to its required type (Listing Six). Given such descriptions, you can write a single, polymorphic function that checks whether a value has a given type; see Listing Seven.
Reflexive facilities also allow a program to manipulate its own environment. For instance, a program can create a "protected environment" in which to run another piece of code. This situation is common in agent-based applications, when a host runs untrusted code received through the Internet (for instance, Web pages with executable content, a trendy thing in these days of Java). Some extension languages have to provide specific support for safe execution, but Lua is flexible enough to do this using the language itself. Listing Eight shows how the whole global environment can be saved in a table. A similar function restores a saved environment. Because all functions are first-class values assigned to variables, it is trivial to remove a function from the global environment. Listing Nine shows a function that runs a piece of code in a protected environment.
Binding Tk to Lua
A natural use of Lua is in the description of GUIs where you need facilities to describe hierarchies of objects (widgets) and to bind user actions to them. Lua is suitable for such tasks because it combines data-description mechanisms with simple, powerful, and extensible semantics. Indeed, we have developed several UI toolkits with Lua.
Although Tk is a versatile GUI toolkit, Tcl isn't the kind of language that everyone feels comfortable with. Since we regard Lua as an alternative to Tcl, we decided to implement a Tk/Lua binding, allowing access to Tk widgets from Lua.
As far as possible, we preserved Tk's philosophy, including widget names, attributes, and commands. Everyone knows how tempting it is to try to improve an existing API, but in the long run, leaving it alone is better for Tk users, because they do not have to learn new concepts. (It's also better for us, because we don't have to write a new manual!)
Creating Tk/Lua Widgets
We have mapped all Tk widgets to Lua. You create a widget describing its
attributes, using Lua table constructors. For instance, Example 3(a) creates a button and stores it in
b is now an object that represents the button. After this
definition, you can use ordinary Lua syntax to manipulate the object. Hence, the
="Hello world from Lua!" changes
the button's label and updates its image if it is already displayed on the
screen. A text-entry widget limited to 20 characters can be created with
=20}. After this
widget is mapped on a displayed window,
e.current contains the current
value assigned by the user to the widget. (We use the current field to store the
widget value, instead of using global variables as in Tcl/Tk.)
Widgets are not automatically mapped on a window. Unlike the Tk/Tcl environment, there isn't the concept of a current window in Tk/Lua. You must create a window (which can be the main window or a top-level widget) to hold the other widgets, and then explicitly map it onto the screen; see Example 3(b).
In this way, users can freely describe their dialogs, even cross-referencing widgets and mapping them when necessary. We have also eliminated the need for packing widgets explicitly, because it is more natural to specify layouts in a descriptive manner. Thus, the window (main and top-level) and frame widgets are used as containers that automatically pack their contents. For instance, to show a message with two bottom buttons, you can write Example 3(c). Besides all regular Tk widgets, we have also implemented two additional canvases, one using a simplified API to Xlib, and another using OpenGL.
Almost all functions provided by these libraries were mapped to Lua. So you can create sophisticated graphical applications that use direct manipulation on custom widgets solely in Lua.
Accessing Widget Commands
All Tk widget commands are implemented as object methods in Tk/Lua. Their names,
parameters, and functionality were preserved. If
lb represents a listbox
("New item") inserts a new
item in the list, following the Tk
insert command for listboxes. On the
other hand, the most-used Tk widget command,
configure, is no longer
needed because its effect is now obtained with simple assignments.
The main and top-level widgets inherit the methods from the window manager. If
w represents a window, then
() has its usual
Behind the Scenes
Implementing Tk/Lua wasn't hard. Using the C interface to Tcl/Tk, we created a service provider and registered it to be accessed from Lua. The Lua code that implements the binding uses an object-oriented approach, with the index fallback, as mentioned earlier. Each widget instance inherits from a class object, with the widget class at the top of the hierarchy. This class provides standard methods used by all widgets. Listing Ten shows the definition of this generic class and its method to set the focus to a widget. It also shows the button class definition.
As you now know, each widget is created with a table constructor. The constructor sets the instance class, creates the widget, and stores it in a global array. However, we also use a small trick--instead of returning the new table, the constructor returns the widget location as a numeric ID (Listing Eleven).
Hence, when Lua tries to index a widget, as in
b.label, it calls a
fallback, because numbers cannot be indexed. This trick gives us complete control
over widget semantics. For instance, if
b is a button (actually,
stores an ID) and you set
b.label = "New label," then the
fallback is responsible for calling the appropriate service command to update the
Listing Twelve shows the Tk/Lua "settable" fallback function. This fallback is called at every attempt to index a nontable value. First, we check whether the first parameter corresponds to a valid widget ID. If it does, then we retrieve the widget table by accessing the global array. Otherwise, we dispatch the occurrence to the previously registered fallback.
The widgets in table
tklua_IDtable have an internal field, called
tkname, that stores the corresponding Tk widget name. This name is used to
invoke Tk commands. We check whether there is a corresponding Tk widget and
whether the indexing value is a valid Tk attribute. If so, we ask the service
provider to change the widget attribute (calling the registered C function
e). The assignment
assures us that we can use the widget table to store values besides Tk
Implementing the "gettable" fallback is similar. In addition to these two fallbacks, Tk/Lua also uses the index fallback to implement inheritance (Listing Five) and the "function" fallback to call widget commands or window-manager commands.
Extension languages are always interpreted, in one way or another. Simple extension languages can be interpreted directly from source code. On the other hand, embedded languages are usually powerful programming languages with complex syntax and semantics. A more-efficient implementation technique for embedded languages is now standard: Design a virtual machine suited to the needs of the language, compile extension programs into bytecodes for this machine, and then simulate the virtual machine by interpreting the bytecodes. We have chosen this hybrid architecture for implementing Lua because lexical and syntactical analysis are done only once, resulting in faster execution. Also, it allows extension programs to be provided in precompiled bytecode form only, resulting in faster loading and safer environments.
The authors are researchers at TeCGraf-Grupo de Tecnologia em Computacao Grafica in Rio de Janeiro. They can be contacted at [email protected]