В статье присутствуют философские рассуждения на тему универсальности Лиспа.
Автор:
Написал: artish   Дата: 2008-09-08 22:02
Комментарии: (0)   Рейтинг:
Пользовательская оценка (от 1 до 10): 3.00   
Проголосовавших: 1 с 2012-11-11 17:51

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

Например, есть простейшая вещь - привязка SQL к основному языку. На Лиспе это можно сделать настолько удобно, насколько это вообще возможно в природе. Рассмотрим на примере C++. Представим себе, что у нас есть sql-запрос, с которым мы хотим работать из программы. Обычно мы его заводим как некий объект, а чтобы обратиться к колонкам, нужно писать что-нибудь вроде

my_query.fieldByName('a').asString

при этом, мы еще и будем рисковать получить во время выполнения три возможных ошибки:
- текст запроса неверен;
- такого поля вообще нет;
- оно есть, но не того типа, который мы написали;

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

Ты заводишь запрос, который может отчасти генерироваться в момент исполнения, но его часть, содержащая перечень выходных колонок, должна давать правильный SQL-запрос в момент компиляции. А, допустим, "order by" вписывается потом. В лиспе можно сделать (и я сделал) так, что на каждую колонку у тебя заводится соответствующая переменная. То есть (перевожу все на C++, потому что синтаксис Лиспа трудно понять новичкам).

Пишем в файле следующее:

developer_sql_connection("my_service","sa",""); // чтобы компилятор C++ подключился к серверу
...
void sql_test(string user_input) {
string xxx="order by "+user_input;
with_sql_query(key("select a,b,c from t")+xxx,my_iterator)
{ // по циклу идем по всем записям
cout << a << "," << b << "," << c <<"\n"; // переменные a,b,c "получились сами"
}
}

как мы этого добились? С помощью макропроцессора.
1. Макропроцессор в момент компиляции встречает developer_sql_connection и коннектится к базе для чтения схемы.
2. Макропроцессор встречает конструкцию with_sql(arg1,arg2) {operators} и делает следующее:
- парсит первый аргумент, находя в нем key(sql). По соглашению, то, что внутри key - это статическая часть sql-запроса.
- выполняет этот sql-запрос (проверяет, что он правильный, т.е., снижает вероятность ошибки времени выполнения), получает список колонок и их типов
- генерирует на основании всей полученной информации примерно такой текст:
{
CQuery my_iterator("select a,b,c from t"+xxx)); // создает запрос и
клиентский набор данных
int a; string b; date c; // эти поля сгенерены из описания колонок
while(!my_iterator.eof()) {
a=my_iterator["a"].asInt; b=my_iterator["b"].asString; c=my_iterator["c"].asDate;
{
// сюда подставляются оперататоры, которые были в with_sql между {}
cout << a << "," << b << "," << c << "\n";
}
my_iterator.next();
}
}

Сэкономлено немало сил, и код стал компактнее. Но ни препроцессор, ни компилятор ни одного языка, кроме Лиспа, насколько я знаю, так делать не умеют, во всяком случае, не так легко. Коренная причина этого кроется в том, что Лисп - это единая система, в которой уживаются интерпретатор, компилятор и все интерфейсы, например, интерфейс к БД. Платой за это является размер Лисп-системы в памяти, но если его сравнивать с современными промышленными программами, то он вполне умеренный. В C++ написано, что препроцессор - это "плохо". Но процессор template-ов тоже так не умеют. Боюсь, что и m4 так не умеет. Да и вообще, поменять компилятор C++, да и любой компилятор, не встроенный в среду вместе с интерпретатором, практически невозможно. А компилятор Лиспа меняется за одну строчку.

Но хватит дифирамбов. На самом деле, примерно такая вещь есть в Оракле. Там напускается специальный эгзешничек, который препроцессит текст. Этот экзешничек нужно запустить, отдать ему исходный текст программы, а результат эгзешничка передать компилятору. Чтобы компилятор обрабатывал уже результат ораклового препроцессора. Обычно среды разработки (а уж makefile - и подавно) позволяют такое встраивание. Из всех способов привязки SQL к C++ этот способ дает наиболее удобный и надежный код. Так вот, в Delphi этого нет, в C# этого, по-моему, тоже нет. Там, где это есть, требуется конфигурирование среды, настройка этого эгзешника. Если ты захочешь какой-то похожий функционал, но не совсем такой, то тебе придется писать свой экзешничек, содержащий, ни много ни мало, препроцессор и парсер С++, который не отличается большой скоростью работы. Чтобы написать такую программу, нужно немало сил. А в Лиспе это пишется очень легко, там всего лишь нужно написать соответствующий несложный макрос (на один-два экрана, максимум). А если нужно поменять что-то, то можно поменять этот макрос. Я это сделал в Лиспе, но никто не заказывает мне программы с SQL на Лиспе (пока что). Поэтому я довольствуюсь лишь тем, что генерирую из Лиспа курсоры для Transact-SQL вместе со всеми переменными, удалением курсора и проверкой @@fetch_status. Естественно, тут тоже возникают сложности с конфигурацией, потому что нужен отдельный, внешний препроцессор для SQL. Поскольку я сейчас много не пишу на Transact-SQL, и поскольку такие вещи удобнее внедрять в начале большого проекта, эта наработка тоже не внедрена "в производство".

На Дельфи все - еще хуже. Приходится писать.

qq:=openSQL('select a,b,c');
qq.fieldByName('a').asString и так далее.

И это никак не обойти даже с внешним препроцессором. Во всяком случае, неудобства будут превышать выигрыш от экономии размера текста и нажатий на кнопки. Особенно это фатально тем, что в Дельфи нельзя объявить переменную где попало - ее нужно объявлять только перед началом тела функции. То есть, без парсера Паскаля никак не обойтись. И в среду там тоже не встроишься со своим препроцессором. В принципе, все это можно было бы додавить, но получилось бы сомнительно по надежности и по другим параметрам. А если бы работать в связке "Лисп-SQL" - была бы просто конфетка!

Получается, что Лисп был бы более пригоден для написания программ работы с базами данных, чем Дельфи, во всяком случае, в этом аспекте.



Онлайн :

0 пользователь(ей), 34 гость(ей) :




Реклама на сайте: