Pull to refresh

Comments 11

Недоделанный перевод (моего авторства) оригинальной статьи
gist.github.com/2919244
Таки так и не осмелился опубликовать таки, ибо все таки не уверен, что будет полезно в серьезных проектах.

На тему генерации кода, у автора англоязычной статьи есть несколько не очень удачных моментов.
В частности, не очень удобно задавать модуль, а намного приятнее описать саму рабочую функцию декоратора через fun. Но обработка такого варианта этого «несколько» сложнее.
Дополнение: «обработка такого варианта» впринципе невозможна. Проблема в том, что аргументом декоратора может быть только литеральный терм. Т.е. fun там запрещен. До нашего parse_transform дело даже не найдет.
Код типа

-d(fun(Fun, Fargs)-> apply(Fun, Fargs) end).
function(X)->
    X.

или

-d(fun Mod:Fun/2).
function(X)->
    X.

будет отвергнут парсером еще до применения parse_transform.
На тему мемоизации, то, что вы реализовали, это скорее безвременное кеширование.
Интереснее было бы через декораторы реализовать настоящую мемоизацию.
что-то типа
gist.github.com/2875252
Через Y-комбинатор. Но не сильно понятно, как на это можно навесить декораторы, так чтобы не пришлось описывать уродским не очень удобным образом функции.
fibimpl(Self) ->
    fun
        (0) -> 1;
        (1) -> 1;
        (N) when N > 1 ->
            ((Self)(N - 1)) + ((Self)(N - 2))
    end.
Согласен. Это скорее кэширование результата.
Но смысл статьи просто продемонстрировать декораторы, а пример — первое что пришло в голову.

Вообще в своем коде я декораторы применяю только для отладки участков кода, а в production они и вовсе отключены.
Кстати, еще одна проблема с parse_transform, в том, что ее можно отключить. Приходится быть внимательнее.
Аналогичный вашему модуль удобно было бы использовать еще для рендеринга страниц и проверки прав доступа.

В вашем случае каскад декораторов отработает корректно?
Да… И порядок имеет значение.
Я, кстати, реализовывал loginRequired декоратор, когда писал проект под Cowboy.
Оно github.com/Egobrain/oauth_cowboy/blob/master/src/oauth_to_functions.erl?

На самом деле Вы классно сделали. Таки, довели до конца задумку Ники.
Единственное, сразу возникает желание завести что-то типа generic decorator
и потом от него наследоваться. Вы об этом думали?
Разобравшись с инфой по ссылке не совсем понял какой профит принесет такой код относительно моего в том случае если декоратор поставить на рекурсивную функцию.

fact(N) when is_integer(N) andalso N>=1  ->
    fact(N,1).

?MEMOIZE. % now decorator is here
fact(1,Acc) -> Acc;
fact(N,Acc) -> fact(N-1,Acc*N).
В таком случае да. Только это не математическое определение. Эффективно, но неудобно.
Хочется писать что-то в духе (хотя это и будет дорого по памяти):
?MEMOIZE.
fact(1) -> 1;
fact(N) when N > 1 ->
    N * fact(N - 1)

Но в этом случае, бессмысленно запоминать промежуточные результаты.
Тут это ничего не даст. Для более «сложных» примеров ваши декораторы тоже выглядят правдоподобно.
?MEMOIZE.
fib(0) -> 1;
fib(1) -> 1;
fib(N) when N > 1 ->
    fib(N - 1) + fib(N - 2)

Ключевой момент тут, что fib(N — 2) должна использовать результаты промежуточных вычислений fib(N — 1). После вычисления целевой функции хранить промежуточные результаты смысла особого не имеет, только конечный. Во общем на то и нацелен каскад непонятных функций в примере. Если их удаться удачно убрать в декоратор, то будет здорово.
Ключевой момент. В вашей реализации декоратор возвращает значение. Но должен функцию. Это позволит комбинировать декораторы в рантайме.
Sign up to leave a comment.

Articles