Немного макросов-операторов на Nemerle
Расширяемые анонимные типы
По мотивам хотелок
![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
def obj = new(name = "anon", age = 10, created = DateTime.Now);
В общий чертах, работает это так: макрос new по переданным параметрам генерирует генерик-класс с макро-атрибутом [Record] вида
[Record] class AnonType[name, age, created] {
public name : name;
public age : age;
public created : created;
}
Макрос [Record] создает конструктор с аргументами для всех полей, а потом вызов макроса переписывается в конструктор только что созданного класса.

Это очень красивое решение, на мой взгляд, потому что объем работы сравнительно небольшой, а всю остальную заботу берет на себе компилятор, типизируя переменные и конкретизируя обобщенный класс.

Я подумал, что раз подобная работа уже была кем-то проделана, то сделать расширяемые анонимные типы будет несложно. Для этого надо узнать тип объекта, который планируется расширить новыми полями, получить информацию о его конструкторе, а потом сложить параметры конструктора с новыми аргументами, а потом отдать все это макросу new.
[assembly: Nemerle.Internal.OperatorAttribute ("NemerleExperiments", "<+", false, 141, 140)]
public macro @<+(var: PExpr, extends : PExpr) {
ExtendedAnonTypesImpl.ExtendBy(Macros.ImplicitCTX(), var, extends)
}
module ExtendedAnonTypesImpl{
public ExtendBy(typer: Typer, var : PExpr, extends: PExpr) : PExpr {
Macros.DefineCTX(typer);
def varName = UtilM.GetName <| var;
def newFields = UtilM.Expr2List <| extends;
def buildExtendCtor(t) {
def flags = BindingFlags.Public | BindingFlags.Instance;
def ctorParams = t.Type.TypeInfo.GetConstructors(flags).Single().GetParameters();
def oldFields = ctorParams.Map(fun(p) {
def pname = <[$(p.Name : usesite)]>;
<[ $pname = $(varName : name).$pname ]> : PExpr
});
def allFields = oldFields + newFields;
<[ Nemerle.Extensions.AnonymousClassNormalCtor(..$allFields) ]>;
}
UtilM.WithType(typer, var, buildExtendCtor)
}
}
Использовать можно в сочетании с макросом new (в принципе, с любым типом, конструктор которого создан макросом Record)

К сожалению, информация о типе будет доступна только в пределах функции или свойства уровня класса, где все это происходит. То есть из локальной функции наверх или в параметры другой локальной функции передать данные без потери информации о типе можно, а вот за пределы члена класса эту информацию я вытащить не смог. Хотя и весьма настырно пытался. Дело в том, что методы класса типизируюся до того, как начинается работа с их телами, когда будет раскрыт макрос new. Ну и вроде бы задача неразрешима, но в одном конкретном случае мне все же удалось пробросить информацию наверх. Может быть, лучшее понимание работы компилятора поможет обобщить это костылик.
Инфиксная форма функции от двух аргументов
По мотивам Haskell с его поистине безграничным синтаксическим сахаром из коробки. Еще давно, когда я только пришел на форум к немерлистам, выяснять чего же умеют в плане синтаксиса макросы Nemerle, я задавал вопрос, как сделать тернарный оператор вида аргумент1 `функция` аргумент2, который бы переписывался в обыкновенный вызов этой функции. Мне сказали, что кроме унарных и бинарных операторов никаких других не предусмотрено, и я успокоился. А тут подумал, что ничего не мешает мне объявить оператор `, а закрывающий ` поймать руками. Ничего и не помешало
[assembly: Nemerle.Internal.OperatorAttribute ("NemerleExperiments", "`", false, 300, 284)]
macro @`(larg, rigth) {
def (f, rarg) = match(rigth) {
| <[ $e1 ` $e2 ]> => (e1, e2);
| _ => Message.FatalError(rigth.Location, "Expected `");
}
<[ $f($larg, $rarg) ]>
}
Единственное, что вызывает сомнения - это корректность выбранной силы связывания. Выражение слева будем забирать последними, если кому-то надо - пусть ставит скобки. А вот справа, необходимо активней поглощать токены, чем . (точка), иначе невозможно будет оборачивать этим оператором функции, находящиеся в модуле или пространстве имен.

Работает как и ожидалось, хотя, я думаю, фича абсолютно бесполезная. Думал, в каких ситуациях это удобно использовать и не придумал ничего. Правда, когда имеешь дело с силой связывания, смотришь силы стандартных операторов, голова идет кругом и в голове только "нинужна".
Именованная лямбда
По мотивам АНАФОРИЧЕСКОЙ ЛЯБДЫ. В моем случае, речь идет о конструкции, которая выглядит как лямбда-выражение, ведет себя, как лямбда-выражение, но при этом имеет имя, по которому к ней можно обратиться. Не очень понятно, что тут можно прокомментировать, берем выражение и переписываем его в лямбду, внутри которой объявляем функцию, куда помещаем тело лямбды, и вызываем получившуюся функцию.
[assembly: Nemerle.Internal.OperatorAttribute ("NemerleExperiments", "~>", false, 145, 120)]
public macro @~>(name, body) {
def (fname, fargs) = UtilM.Expr2FunCall <| name;
def pargs = fargs.Map <| UtilM.Expr2Parm;
<[ fun(..$pargs) {
def $(fname: name)(..$pargs) $body;
$(fname: name)(..$fargs);
} ]>
}
Реализация, по-моему, короче описания на словах.

Именованная лямбда во всей красе! Вычисление факториала рекурсивным вызовов прямо на месте! Боюсь, на этом
Класс UtilM в примерах мой, я недавно начал собирать туда всякие штуки, которые можно заподозрить в повторном использовании.
Макросы-операторы не обязательно должны представлять собой хаскиарт, можно давать нормальные полноценные имена, вроде with или extend. Надо было бы продемонстрировать, но редактировать написанное слишком лениво.
Извиняюсь за черный фон фрагментов кода, но я пытался переключиться на светлую тему студии, а у меня глаза вытекли.