Type Introspection

lua-users home
wiki

Type introspection [1] is the ability determine the type of an object at runtime. This page describes ways this can be supported in Lua.

First, why do we need type introspection? It may be that you don't even need to use type introspection at all. Duck typing [2] and [polymorphism] can dispatch based on type without programmer intervention. There are reasons you may still want to inspect type, such as for debugging purposes [3] or type checking (LuaTypeChecking). You may also want to dispatch code in a particular custom way, such as how [string.gsub] behaves differently depending on whether repl is a string, table, or function. Unfortunately, the lines are sometimes blurred, such as FuncTables, which are tables with a call operator allowing them to behave like functions. Also, maybe you want to define a function that loads data from either a string or a file specified by filename, but both are strings, so you need some other way to distinguish them (e.g. two separate functions like [loadstring] and [loadfile]).

The [type] function returns the basic type of value as a string. There are only eight basic types in Lua ("nil", "number", "string", "boolean", "table", "function", "thread", or "userdata").

The [lua_type] function in the C API is similar to type but returns an integer constant. It also differentiates between the two types of userdata: LUA_TUSERDATA and LUA_TLIGHTUSERDATA (see LightUserData). There is also a lua_typename function that converts this integer back to a string in the form returned by type.

The [io.type] function determines whether the given object is a file handle (as well as whether it is open or closed). A number of user libraries follow a similar pattern by providing their own function that checks whether the given object is of a type defined by the library, either returning true/false or a class name string. This function is implemented using methods described below. This style can, however, go against duck typing and can prevent (LuaVirtualization).

The equality operator, or more precisely the [rawequal] function can check if two values have the same identity (and by implication are the same objects since objects have unique identity). If all objects in a class are stored as keys in a table, then you check for identity by indexing the table (preferably weak keyed table, although prior to Lua 5.2 that has a caveat--GarbageCollectingWeakTables):

local isfoo_ = setmetatable({}, {__mode='k'})

function newfoo()
  local self = {}
  isfoo_[self] = true
  return self
end

function isfoo(o)
  return isfoo_[o]
end

If all objects in a class, and only those objects, have a certain metatable (or value returned by a __metatable metamethod), then you can check that metatable:

local foo_mt = {}

function newfoo()
  local self = setmetatable({}, foo_mt)
  return self
end

function isfoo(o)
  return getmetatable(o) == foo_mt
end

To obtain the name of a user-defined type as a string, you might first obtain the type of the object and then obtain the name from the type. You might do to this via tostring on the type, but some consider tostring only for debugging. Or you might store a _NAME field in the type or even in the object itself [9][4] (TypeOf). The reason for this choice of field name is that a module's _NAME field is set by the [module] function, and a module is often used as a type, and even those not using the module function (LuaModuleFunctionCritiqued) may still follow the convention of using _NAME. Unfortunately, indexing an object might raise an error rather than return nil (duck typing can also have this problem).

Some have modified the type function to support user-defined types, possibly by querying a __type metamethod [5][6]. This may break existing code that expects only basic types returned or add some overhead globally. You could, however, swap in a custom type function locally: local type = mytype . Others have made user-defined types (or subtypes) be returned only as a second return value of type [7][8].

Some object-oriented frameworks (ObjectOrientedProgramming) implement their own introspection capabilities, but these may not be compatible with objects not created with that framework.

Not all these type introspection methods are fool-proof against intentional abuse (see SandBoxes). An object's identity, however, cannot be forged in normal Lua usage. Also, by restricting access to the debug library and providing a __metatable metamethod, can can prevent public access to an object's metatable, as well as public knowledge of the metatable's identity. These can hinder abuse from untrusted code.

--DavidManura

Note: for a relatively simple function that will allow you to print diverse objects without Lua crashing, see IntrospectionFunctionsLua.


RecentChanges · preferences
edit · history
Last edited April 6, 2013 4:51 pm GMT (diff)