lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On 15 Feb 2012, at 07:50, steve donovan wrote:
> Inspired by Jay's challenge to keep it simple & stupid, I've come up
> with thirty-odd functions that cover a lot of the general boilerplate
> code we end up writing. 'Microlight' because it's the trimmed-down
> younger brother of Penlight, about 500 lines in total, of which 160
> are doc comments.

Here goes a view of such functions from a very *personal* perspective:

split -> I rather use 'string.gmatch'.

escape -> nice one indeed (I assume magic characters are the "magic" characters of Lua string patterns, right?).

expand -> I've only needed such thing in stand-alone applications, mainly to support internationalization of user messages. In such case, I rather add such function in the 'string' table to use syntax 'message:expand(tags)' and use some pretty-printer customized for the particular application that allows to use complex values to replace tags.

readfile -> I rather use 'io.open'/'file:read("*a")' and get proper error messages from the 'io' library.

exists -> I rarely need to only check for the existence of a file, I usually need to read it or write it and can check for its existence while accessing, and also get the error messages.

splitpath, splitext -> interesting, but what types of paths does it support? Unix and Windows? I avoid processing paths as a general rule because Unix file paths are not as portable as I would like.

tstring -> I think this is really complicated. My solution for this is a parametric algorithm that I customize differently for every use (see loop.debug.Viewer).

tdump -> serialization has similar problems to visualization. It's hard to know what to serialize and what not, how to allow custom serialization for complex values (see loop.serial.Serializer).

update -> if I understand what this function is supposed to do, what I use instead is:

--------------------------------------------------------------------------------
-- Copies all elements stored in a table to another.
-- 
-- Each pair of key and value stored in table 'source' will be set to table
-- 'destiny'.
-- If no 'destiny' table is defined, a new empty table is used.
-- 
-- @param source Table containing elements to be copied.
-- @param destiny [optional] Table which elements must be copied into.
-- 
-- @return Table containing copied elements.
-- 
-- @usage copied = table.copy(results)
-- @usage table.copy(results, copied)

function table.copy(source, destiny)
	if source ~= nil then
		if destiny == nil then destiny = {} end
		for key, value in next, source do
			rawset(destiny, key, value)
		end
	end
	return destiny
end

All other table funcs -> these are very common indeed, but I rather use the basic Lua constructs that are more efficient and express their precise semantics better than a function name.

throw/safe -> I rather use 'assert(f(...))'/'pcall(f, ...)', or write specific wrapper functions that adjust the function contract to the model of the code I'm writing (e.g. converting string errors to structured exceptions).

bind1, compose -> I never felt the need to use such thing.

callable -> really nice one. But I'd rather see something like a separate library also with functions like 'indexable', 'newindexable', etc. (yes, these names are awful ;-)

class -> This one I use a lot (see LOOP and OiL), but it may be more problematic than the functions above in the sense of obscure semantics for people not familiar with it. From my experience with LOOP, experienced Lua programmers usually wonder what exactly 'class' does (maybe because libraries that support OO in Lua are usually bloated with complex semantics borrowed from huge OO languages like Java), and they are usually worried about weird stuff being done under the hood, so it is safer and clearer for them to write their own simple version of a 'class' function, which is usually all they need. Moreover, programmers coming from OO languages usually miss some particular feature not readily provided by the 'class' function.

is_a -> I guess this is only a natural consequence of adopting a class model with inheritance.

type -> I can't recall the explicit need for such thing. I believe I'd rather use a 'callable' or 'indexable' instead of asserting for a particular metatable or type. But I'm not sure.

> There is no 'shallow copy', but it's easy to make such a function.

Functions like 'table.copy' and 'table.clear' seem basic and general enough to make into a community standard library, specially if written in C like the official standard libraries. One function that I do use *a lot* is:

--------------------------------------------------------------------------------
-- Creates a memoize table that caches the results of a function.
-- 
-- Creates a table that caches the results of a function that accepts a single
-- argument and returns a single value.
-- 
-- @param func Function which returned values must be cached.
-- @param weak [optional] String that defines the weak mode of the memoize.
-- 
-- @return Memoize table created.
-- 
-- @usage SquareRootOf = table.memoize(math.sqrt)
-- @usage CounterOf = table.memoize(function() return 0 end, "k")

function table.memoize(func, weak)
	return setmetatable({}, {
		__mode = weak,
		__index = function(self, input)
			local output = func(input)
			self[input] = output
			return output
		end,
	})
end

--
Renato Maia
Computer Scientist
Tecgraf/PUC-Rio
__________________________
http://www.tecgraf.puc-rio.br/~maia/