15 сент. 2007 г.

Использование декораторов в Lua

Дэвид Манура (David Manura) предложил интересную технику использования декораторов в Lua. (По крайней мере, его письмо — первое, в котором я её увидел.)

Рассмотрим простейший пример того, что можно сделать при помощи декораторов:
local foo =
print_start_end(printor 'begin foo', printor 'end foo')
..
function (buzz)
print ('FOO called with ' .. buzz)
end

foo('ARGUMENT')

Вывод:

begin foo
FOO called with ARGUMENT
end foo

Реализация:
function printor(msg)
return function() print(msg) end
end

function print_start_end(prologue, epilogue)
assert(type(prologue) == 'function'
and
type(epilogue) == 'function')
local M = {}
setmetatable(M, {
__concat = function(lhs, rhs)
assert(type(rhs) == 'function')
return function(...)
prologue()
local result = rhs(...)
epilogue()
return result
end
end;
})
return M
end

Главное достоинство такого метода — в «удобоваримом» синтаксисе без необходимости привлечения метамеханизмов.

Правда, используемый подход не позволяет пользоваться синтаксическим сахаром для объявления функций и методов, но это небольшая беда. Вот к такой записи не прикрутишь декоратор:
function bar(buzz)
print('BAR called with ' .. buzz)
end

foobar = {}
function foobar:buzz(foo)
print('foobar:buzz called with ' .. foo)
end

Один из вариантов применения такого декоратора — проверка входных и выходных параметров функции (и, шире, пред- и постусловий вообще).

Например (первое, что пришло в голову):
local string.rep =
docs
[[Returns a string that is the concatenation
of n copies of the string s.]] ..
args { 'string', optional 'number' } ..
rets { 'string' } ..
function (s, n)
n = n or 1
if n > 1 then
local t = {}
for i = 1, n do
t[i] = s
end
s = table.concat(t)
end
return s
end

Но об этом — в следующий раз.

Комментариев нет: