AWL (язык программирования)

Материал из Seo Wiki - Поисковая Оптимизация и Программирование

Перейти к: навигация, поиск
AWL (Alternative Web Language)
Класс языка:

мультипарадигмальный: функциональный, процедурный, объектно-ориентированный

Тип исполнения:

интерпретируемый

Появился в:

2005 г.

Типизация данных:

динамическая

Диалекты:

отсутствуют

Испытал влияние:

LISP, Scheme, Perl, Ruby, Снобол, APL

AWL -- новый сценарный язык программирования интерпретируемого типа.

Он реализует множество идей, заимствованных из таких языков, как Perl и Ruby (элементы синтаксиса, общая идеология числовых и строковых операций), LISP (ортогональность данных и выполняемого кода, базовая семантика списков), многих языков функционального программирования (полноправность функциональных значений, возможность их генерации в процессе выполнения), Снобол (семантика и реализация образцов) и APL (набор операций над массивами).

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

Язык распространяется бесплатно. По мере развития проекта, исходные тексты планируется опубликовать под одной из свободных лицензий (в настоящее время они доступны только для активных разработчиков).

Идеи и концепции

Как и во многих языках сценариев, в AWL отсутствует строгая типизация, типы всех переменных и значений полностью динамические. Для языка характерен идейный минимализм («меньше сущностей без необходимости»). Например, нет типичного разделения сущностей языка на операции, функции, операторы: в AWL вместо всего этого предусмотрено одно обобщенное понятие: функтор. Как и функция, функтор вызывается с передачей ему списка аргументов, но в AWL их вычисление в общем случае является ленивым (то есть рассматривается как ответственность функтора). Большая часть семантического ядра языка воплощена в наборе предопределённых функторов (как в LISP и Forth), и в AWL в принципе отсутствуют такое понятие, как «ключевые слова».

Например, все операции над числами и строками (примитивными типами данных языка) реализованы как стандартные функторы, вычисляющие операнды перед вызовом. Так, результатом min(a+b, c+d) является меньшее из значений a+b и c+d; результатом s_cat(“Hello ”, s_cat(user, “!”)) — конкатенация строк «Hello », user и «!»; и т. п. Для большинства таких операций имеется более традиционный и компактный синтаксис (например, A ?< B вместо min(A, B) или R +$ S вместо s_cat(R, S)). Независимо от внешнего синтаксиса кода, при псевдокомпиляции он всегда преобразуется во внутреннюю (то есть функциональную) форму.

В языке есть управляющие операции, которые не предполагают безусловного вычисления операндов. Например, в условной операции if(condition, then, else), если результат condition истинен (ненулевой), вычисляется then, иначе else (но никогда не оба операнда сразу). Вычисленное значение возвращается в качестве результата, то есть функтор if реализует логику как условного оператора (if condition then then else else), так и условного выражения в C, Java или Perl (condition ? then : else). (Последний синтаксис допускается и в AWL.)

Циклические операции (в терминологии AWL, итераторы) способны вычислять свои операнд(ы) многократно. Например, функтор while(condition, loop) реализует логику цикла «с предусловием» (аналогичного оператору while в C или Pascal): повторно перевычисляются условие condition и тело цикла loop, пока condition остается истинным. Набор операций-итераторов в AWL довольно богат: например, l_loop(p, List, loop) организует последовательный перебор элементов списка List (присваивая каждый переменной p, и выполняя тело loop). Аналогично, выполнение for_inc(I, From..To, loop) организует перебор всех целых значений в диапазоне от From до To (включая From, но не To), последовательно присваивая их переменной I с выполнением тела цикла loop. Заметим, что не предусмотрено операций явного или неявного перехода, типа goto, break или continue: единственный механизм, позволяющий нарушить жестко заданный порядок выполнения — исключения (доступные в новых версиях языка). Все итераторы также возвращают значение (обычно, последнее вычисленное значение тела цикла). В AWL значение возвращают даже блоки: т.к. блок { S1; S2; S3Sn } — это последовательность вычислений S1…Sn, в которой значение Sn является результатом (что позволяет использовать блок как операнд выражения, например). Резюмируя, можно сказать, что в AWL понятия «выполнение» и «вычисление» принципиально не различимы (как и во многих функциональных языках, например в Nemerle). Всё, что выполняется (выражения, операторы, блоки) всегда возвращает результат (который, конечно, может игнорироваться). Когда явное значение отсутствует, им является пустой список (), или undef (это значение имеют все переменные без инициализации).

Механизм ленивого вычисления может использоваться для явного управления выполнением кода (в отличие от «неуправляемого» ленивого вычисления в языках типа Haskell). Унарная операция ‘@’ («девальвация») «подавляет» вычисление операнда: в результате my_expr = @(x*x + y*y) переменной my_expr присваивается само выражение, а не его результат. Обратная операция ‘^’ («ревальвация») завершает ранее «подавленнное» вычисление: например, вычисление ^my_expr равносильно (x*x + y*y) (при значении my_expr определённом как выше). Фактически, все выражения (вызовы функторов, блоки и т. п.) в AWL также являются разновидностью данных (в языке даже присутствуют средства для их динамического формирования при выполнении).

Помимо перечисленных выше типов данных, в AWL доступны массивы, хэши (словари), образцы и потоки. Массивы в AWL могут быть многомерными, и допускают элементы различных типов. По сравнению с вложенными списками, они обеспечивают более быстрый доступ к элементам. Доступно несколько функторов, обеспечивающих конструирование и перестройку массивов. Словари — это неупорядоченные наборы пар «ключ-значение» с возможностью быстрого доступа по ключу (и ключи, и значения допустимы произвольного типа). Регулярные выражения (образцы) в AWL не задаются строками (как в Perl или Python), а создаются специальными функторами-конструкторами. (Для примера: rx_alt(«Здравствуй», «Привет») возвращает образец, сопоставляющийся с «Здравствуй» или с «Привет», а rx_rep(5..10, 0, ‘*’) — образец, сопоставляющийся с повторением ‘*’ от 5 до 10 раз подряд.) (Операции для преобразования регулярных выражений, заданных строками формата PCRE, уже реализованы в стандартных библиотеках.) Наконец, потоки могут быть связаны с файлами и сетевыми ресурсами, обеспечивая выполнение операций ввода/вывода.

Для определения новых функторов используется следующий общий синтаксис:

! myfunc (parameters …): [locals …] = result;

Таким образом определяется функтор myfunc с параметрами parameters (они допускают инициализаторы по умолчанию) и локальными переменными locals, результатом которого является вычисление выражения result. Например, чтобы вычислить радиус сферы, проходящей через точку (X, Y, Z), можно использовать:

! radius (X Y Z) = sqr (X*X + Y*Y + Z*Z);

Чаще телом функтора является блок: например, вычисление Sum_List(List) возвращает сумму всех элементов списка-аргумента List:

! Sum_List (L): [v sum] = {
 Sum = 0;
 l_loop (v, List, Sum = Sum + v);
 Sum };

При описании функтора можно включить для любого из параметров ленивую передачу (поставив перед ним ‘@’) — в этом случае соответствующий аргумент передаётся без вычисления. Таким образом можно легко расширять набор управляющих конструкций языка. Например, несложно определить итератор для цикла с параметром var, изменяющимся от From до To с произвольным (возможно, не целым или отрицательным) шагом Step:

 ! for_float_range (@var From To Step @Loop) = {
  Step > 0 ? {
   ^var = From;
   while (^var <= To):: { ^Loop; ^var =+: Step; }
   } :
  Step < 0 ? {
   ^var = To;
   while (^var >= From):: { ^Loop; ^var =+: Step; }
   } :
 };

Все функторы могут вызываться не только напрямую, но и косвенно, по ссылке. Чтобы сослаться на именнованный функтор (встроенный или определённый), используется синтаксис !func. Со функциональными ссылками можно работать, как с любыми другими значениями: присваивать переменным, передавать в функторы и возвращать из них и т. п. Для вызова функтора, доступного по ссылке func_ref, с аргументами args, используется запись func_ref ! (args). Например:

 (flag ? !add : !sub) ! (xx, yy)

возвращает сумму или разность значений xx и уу (в зависимости от истинности flag). Чуть менее тривиальный пример:

 (flag ? !for_inc : !for_dec) ! (I, From..To, call_me(I))

вызывает call_me для всех целых значений между From и To (в возрастающем или убывающем порядке, в зависимости от истинности flag). В качестве функциональных ссылочных значений могут также использоваться анонимные функторы (аналоги лямбда-определений LISP или Python). Например, l_map (!(x) = (x*3), List) возвращает как результат список значений List, умноженных на 3.

Наконец, в языке есть инструментарий для объектно-ориентированного программирования. Все объекты в языке являются экземплярами классов, описание и поведение которых имеет много аналогий с функторами. Как и функторы, классы могут иметь набор инициализируемых аргументов, локальных переменных (компонент класса), и локальных функторов (методов класса). (Фактически, описывая новый класс, мы одновременно создаем стандартный функтор-конструктор для всех объектов этого класса). (Описания класса могут содержать конструкторы и деструкторы, для корректной инициализации и уничтожения экземпляров класса.)

 !! ChessBoard (Side) : [Data]
   = (Data = array (Side, Side))
   {
   ` (следует описание методов для ChessBoard …) `
   };

Теперь, например, ChessBoard(8) создаёт новый объект (шахматную доску 8*8). Поскольку AWL — слабо типизованный язык, то обращения к компонентам или методам класса должны сопровождаться указанием на то, с каким классом происходит работа: квалификаторами вида Class!!method или Class!!member. (За счёт того, что эта информация доступна при компиляции, код выполняется более эффективно.) Однако (в отличие, например, от C++) квалификация применима к целому блоку кода, как в данном случае:

 ChessBoard!! {
   for_inc (I, 8, Data{0, I} = Pawn(White));
   for_inc (I, 8, Data{7, I} = Pawn(Black));
   Data{0, 0} = Data{0, 7} = Rook(White);
   Data{7, 0} = Data{7, 7} = Rook(Black);
       ` (и т.д. …) `
   };

(Конечно, когда код находится внутри декларации класса, снабжать его явным квалификатором не нужно.) Похожим образом, только во время выполнения, осуществляется связывание класса с его экземплярами: выполнение object.code выполняет произвольный код code применительно к object (делая последний временно, пока выполняется code, активным экземпляром своего класса).

В языке допустимо наследование (но не более от одного суперкласса); в иерархии классов допустимо переопределение виртуальных функторов, обеспечивающее полиморфное поведение экземпляров класса.

Средства графического и веб-программирования

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

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

` fractional part of X `
!frac (X) = X - int(X);

` Waves generator (with location=(X,Y) and wavelength=Lambda) `
!! Generator (X Y Lambda) {
  ` calculate phase `
  ! calc_phase (x y) = abs(frac(rad(x-X, y-Y)/Lambda) - 0.5) * 2
  };   ` Generator `

` Interference widget `
!! [Widget] Interference (BaseColor)
   ` virtual: calculate phase for point (X,Y) -> 0..1 `
   # { calc_phase }

   {
   ! #on_paint (L_T R_B) : [x y] = {
       graphics!!fill_color (BaseColor * 128):: fill (L_T, R_B);

       for_inc (y, L_T[1]..R_B[1],
               for_inc (x, L_T[0]..R_B[0],
                     graphics!!pixel ([x y], BaseColor * int(255 * calc_phase([x y])))
                             )
                     )
                }		` -- #on_paint `
       };		` [Widget] Interference `

` (Two waves sources) `
!! [Interference] Inter2 (Source1 Source2) {
   ! #calc_phase (pt) = Generator!!
       (Source1.calc_phase(pt) + Source2.calc_phase(pt)) / 2
   };

Из примера легко видеть, что управление атрибутами вывода графики осуществляется в "функциональном" стиле (в отличие от более традиционного для систем графического программирования объектно-ориентированного).

Аналогичный подход может применяться для создания динамических электронных веб-документов (даже набор форматирующих операций, определённый для документов, отчасти пересекается с операциями для графического вывода). Здесь AWL позволяет интегрированно решать две задачи (собственно создание текстового документа, имеющего разметку и форматирование, а также генерацию и преобразование данных), которые в обычных веб-технологиях решаются отдельно, и за счёт разных, слабо интегрированных друг с другом средств.

Например, если данные определены в виде списка:

List = ('item1', 'item2', 'item3', ... 'itemN');

очень легко преобразовать их в форматированный список, включённый в документ:

N_List (l_loop (l, List, L_Item (l)));

Аналогичный подход применим и к таблицам: если двухмерная таблица задана в виде 2D-массива Array, то она преобразуется в печатное представление следующим кодом:

Table (for_inc (row, a_dims(Array)[0], T_Row (for_inc (col, a_dims(Array)[1], T_Cell (Array {row, col})))));

Если требуется, например, дополнить таблицу сверху и слева номерами строк/столбцом, приведённый код очень легко модифицировать:

[n_rows n_cols] = a_dims(Array);
Table ({
  ` заголовок таблицы `
  T_Row ({T_Cell (); for_inc (col, n_cols, T_Cell (col + 1))});
  ` тело таблицы `
  for_inc (row, n_rows, T_Row ({ T_Cell(row + 1); for_inc (col, n_cols, T_Cell (Array {row, col})) }))
  });

Ссылки

en:AWL (programming language)
Личные инструменты

Served in 0.406 secs.