вторник, 13 марта 2012 г.

with-sanity

Как часто вам, посоны, приходилось писать что-то вроде
SomeBoilerplate(1);
MoreBoilerplate("Some parameter");
TheCodeThatYouActuallyNeed();
YetAnotherPieceOfBoilerplate(Foo.Bar);
а чуть более в другом месте
SomeBoilerplate(0);
MoreBoilerplate("Other parameter");
YouNeedThatCodeToo();
YetAnotherPieceOfBoilerplate(Foo.Baz);
?
Сразу видно, что можно бы завернуть все это дело в функцию, принимающую параметры для бойлерплейтного кода... вот только действительно нужный код везде разный и в лучшем случае нам удастся свести три строки бойлерплейта к двум. Это, конечно кажется выходом, когда лишних строк больше десяти, но все равно, надо о них помнить и надо их писать (или копипастить).
Так вот, пока овцеебы из Вильяриба наворачивают на это слои говна абстракций из классов, интерфейсов и генериков, щедро сдабривая все это паттернами, веселые ребята из Вильябахо не страдают хуйней и пишут макрос вроде этого:
(defmacro with-boilerplate (number string enum &rest body)
  `(progn
     (some-boilerplate ,number)
     (more-boilerplate ,string)
     ,@body
     (yet-another-piece-of-boilerplate ,enum)))
Применяется он так:
(with-boilerplate 1 "Some parameter" 'foo-bar
                  (the-code-you-actually-need))
(with-boilerplate 0 "Other parameter" 'foo-baz
                  (you-need-that-code-too))
Правда, охуенно?
В F# это делается примерно так же:
let WithBoilerplate aNumber aString aEnum aBody =
    SomeBoilerplate aNumber
    MoreBoilerplate aString
    aBody ()
    YetAnotherPieceOfBoilerplate aEnum

WithBoilerplate 1 "Some parameter" Foo.Bar (fun () -> TheCodeThatYouActuallyNeed())
WithBolierplate 0 "Other parameter" Foo.Baz (fun () -> YouNeedThatCodeToo())
Правда, это уже не будет раскрываться во время компиляции и мы понесем все тяготы и лишения -- пролог и эпилог на байтоебском спике -- которые связаны с вызовом функции. Впрочем, это уже проблема компилятора и байтоебов.
К сожалению, в C# мы похожие макросы получим разве что через пару лет (а в Java -- сразу после никогда) когда то, что сейчас стыдливо прозывается Roslyn будет более-менее широко применяться, но и сейчас в нем можно сделать кое-что похожее:
using System;

public class WithBoilerplate
{
    public static void Do(int Number, string Str, Foo BarOrBaz, Action UsefulCode)
    {
        SomeBoilerplate(Number);
        MoreBoilerplate(Str);
        UsefulCode();
        YetAnotherPieceOfBoilerplate(BarOrBaz);
    }
}
WithBoilerplate.Do(1,"Some parameter",Foo.Bar,() => TheCodeThatYouActuallyNeed());
WithBoilerplate.Do(1,"Other parameter,Foo.Baz,() => YouNeedThatCodeToo());

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

Отправить комментарий