Почему макросы Nemerle не являются злом?
От: VladD2 Российская Империя www.nemerle.org
Дата: 24.04.11 02:14
Оценка: 69 (8)
Среди тех кто не использовал макросы Nemerle на практике есть те кто верит и распространяет одни миф. Звучит он так «Введение в язык средств метапрограммирования опасно. В конечном итоге приведет к адовому кол-ву несовместимых языков » (http://www.rsdn.ru/forum/philosophy/4246218.aspx
Автор: Lloyd
Дата: 23.04.11
).
Оставшаяся часть данного сообщения будет посвящена развенчанию данного мифа. Я вывел ее в отдельную тему, чтобы она не потерялась в потоках флуда развившегося в указанной теме. Ну, и чтобы выделить дискуссию и направить ее по одному руслу.
Для начала некоторая информация о макросах. Без нее трудно понять суть умозаключений последующих ниже.

Макросы располагаются в отдельных сборках

Макрос – это плагин к компилятору. В Nemerle макрос это такой же код как и любой остальной, а значит перед исполнением его нужно скомпилировать и поместить в исполняемую сборку.
Для того чтобы использовать макрос в некотором проекте сначала нужно подключить (к этому проекту) сборку в которой он описан.
Таким образом макрос не может случайно или незаметно появиться в программе.
Стандартные макросы расположены в стандартной макро-сборке Nemerle.Macros.dll идущей в поставке компилятора. По умолчанию она подключается к любому проекту Nemerle. Но ее загрузку можно предотвратить. В этом случае проек будет использовать так называемый базовый Nemerle.
Пользоваться базовым Nemerle очень не просто. То что большинство Nemerle-программистов понимают под Nemerle включает в себя и набор стандартных макросов. Так, например, в базовом Nemerle нет не только ни одного вида цикла, но даже операторов if/else и даже операторов || и &&. По этому обычно проект использует Nemerle.Macros.dll или ее замену (если есть необходимость сильно переделать расширенную часть языка).

Макросы располагаются в пространствах имен

Как и обычные типы макросы располагаются в некотором пространстве имен. По этому перед тем как использовать макросы (если он не объявлен в глобальном пространстве имен) пространство имен в котором он объявлен необходимо открыть с помощью директивы using.
В стандартной библиотеке есть набор макросов которые находятся в глобальном пространстве имен и стало быть доступны без предварительного открытия. Это в основном макросы реализующие стандартные операторы языка (for, foreach, if/else, ||, &&, += и т.п.).

Метапрограммирование и макросы != расширение синтаксиса

Надо отметить то факт, что не всякое метапрограммирование и макросы приводят к расширению синтаксиса. Так в Nemerle поддерживаются макросы в виде атрибутов (аналогичных custom attributes в C#, но приводящим не к генерации метаданных, а к вызову некоторого компиляторного плагина) и в виде вызова функций (обращение к макросу заменяется на результат возвращаемый макросом во время компиляции).
Указанные виды макросов не расширяют синтаксис языка и соответствуют всем правилам разрешения неоднозначностей (аналогичным таковым в C#). Например, вы можете задать полностью квалифицированное имя при обращении к макросу. Так, например, вместо:

using MyMacros;
...
MyMacro(42);

Можно написать:

MyMacros.MyMacro(42);

Неоднозначности

Неоднозначности в программах возникают отнюдь не только при использовании макросов. Так в языках вроде C#, VB (да и в Nemerle) легко написать библиотеку которая будет конфликтовать с другой.
Например, если мы опишем два метода расширения в двух разных классах (пример на C#):

Namespase Ns1
{
  public static class A
  {
    public static class string ExtentionMethod(this string sourse) { return "*" + sourse + "*"; }
  }
}

Namespase Ns2
{
  public static class B
  {
    public static class string ExtentionMethod(this string sourse) { return "!" + sourse + "!"; }
  }
}

И попытаемся обратиться к ним:

using Ns1;
using Ns2;

public static class Program
{
  static class void Main()
  {
    "test".ExtentionMethod()
  }
}

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

Ns2.A.ExtentionMethod( "test");

Еще одним выходом может быть создание нового метода расширения с уникальным именем. Из этого метода можно вызвать необходимый метод используя полную квалификацию его имени.

Неоднозначности и макросы

Из сказанного выше не трудно понять, что для не синтаксических макросов работают все те же правила, что и для обычных методов или методов расширений. Вы точно так же можете квалифицировать имя макроса (указать пространство имен в котором он объявлен) или написать новый макрос-обертку снимающий конфликт имен.
Но что же делать с макросами имеющими синтаксис конфликтующий с другими синтаксическими макросами?
Все очень просто. Если такое случилось, то один из макросов можно вызвать используя нотацию вызова фукнции. При этом вы не сможете воспользоваться синтаксическим расширением, но сможете квалифицировать имя макроса избежав тем самым конфликта имен или синтаксиса.
Это так же дает возможность написать синтаксический макро-обертку не создающий конфликт, но раскрывающийся в обращение (в не синтаксической форме) к исходному макросу.
Таким образом конфликты имен макросов решаются точно так же как и для обычных типов и методов.

«Адово» количество несовместимых «языков»

Теперь перейдем собственно к основному мифу.
К данному моменту вы должны понимать, что создать полностью несовместимые «языки» вы не в состоянии. Вопрос разрешения конфликтов может решить любую несовместимость.

Учитывая, что базовый язык и стандартная библиотека развиваются по принципам похожим для развитие монолитных языков – новые возможности попадают в язык не сразу, а после тщательного обдумывания, проектирования и тестирования, то бояться за то что «зоопарк» случится в стандартной библиотеке макросов не стоит.
Остается вопрос конфликта между библиотеками макросов третьих лиц и библиотеками создаваемыми в рамках проекта разработки ПО в котором применяется Nemerle.
Несомненно конфликты между такими библиотеками возможны. Но они ничем не отличаются от конфликтов обычных библиотек. Разработчики вольны выбирать макро-библиотеки которые они хотят использовать и использовать их там где нужно (в тех файлах проекта где в них есть необходимость.

Отличия от монолитных языков

В отличии от монолитных языков где синтаксис появившись единожды уже не может быть отброшен, и в которых синтаксис «виден» во всех файлах проектов, в языках поддерживающих макросы введение нового синтаксиса не влечет столь серьезных и не обратимых последствий.
Так в разных фалах одного и того же проекта можно использоваться макросы конфликтующие друг с другом синтаксически. Более тог в разных файлах можно использовать даже разные версии одного и того же макроса (если в это есть необходимость).
Кроме того в любой момент вы вольны отказаться от использования макроса или все макро-сборки. Для этого достаточно будет убрать открытие соответствующих имен и/или отключить макро-сборку содержащую эти макросы.

Жизнь без оков

Как не странно макросы снимают кучу проблем с авторов языка. Новая возможность может быть легко реализована в виде отдельной сборку и без долгого тестирования выложена на суд пользователей. Если пользователи не выразят негативного отношения к новинке, можно перенести макрос в стандартную библиотеку. В прочем можно оставить новшество в отдельной библиотеке и просто включить ее в состав инсталлятора Nemerle.
Тот же подход можно использовать и в рамках больших проектов. Управляющими проекта может быть создана стандартная библиотека макросов проекта в которую после предварительного тестирования будут включаться макросы третьих лиц или самописные макросы необходимые для проекта.
Другие макро-библиотеки можно попросту запретить использовать (или создать список допустимых к использованию макро-библиотек).

Заключение

Путем не хитрого логического анализа можно легко понять, что озвученные страхи являются не более чем мифами. Макросы не только не создают проблем, но наоборот позволяют их решать. Программисты не должны ждать появления новой фичи в языке. И не должны страдать от того, что появившаяся фича может навредить их проекту. Они вольны сами управлять тем что за фичи используются в проекте и создавать собственные решения.

Конечно среди макросов могут быть конфликты. Но эти конфликты не фатальных. Ими можно управлять, а от неудачного макроса (или макро-библиотеки) можно избавиться точно так же как от неудачной библиотеки.
Нет никакой разницы между библиотеками функций и макро-библиотеками. Точнее вся разница заключается только в том когда и где загружается данная библиотека. Макро-библиотека загружается на исполнение в рамках процесса компиляции в то время как обычная библиотека загружается компилятором для чтения метаданных, а на исполнение она загружается при старте приложения.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.