Файлопомойка Маздайщика

Моё письмо в рассылку refal@botik.ru. Некоторые замечания и уточнения я буду записывать как цитаты.

Комментарии Аркадия Климова выделены как цитаты с припиской Арк

From: Александр Коновалов <a.v.konovalov87@mail.ru>
Sent: Wednesday, February 20, 2019 8:41 PM
To: refal@botik.ru
Subject: RE: Сравнение веток Рефала

 

Как я там писал?

«Сам я его могу написать, но, наверное, не сегодня. И, если напишу, то он будет неизбежно субъективным. Но обсудить будет весело.»

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

 

(Получилось очень длинно. Интересно, кто-нибудь дочитает до конца?)

 

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

Бенчмарки к разным реализациям не делал, поэтому оценивать быстродействие не буду. Также принципиально пишу по памяти, в источники не лезу (поэтому ссылок не будет). Поскольку пишу по памяти, могу напутать и ошибиться. Поправляйте!

Рефал-2

Я видел только реализацию, которую поддерживает Леонид Белоус, поэтому буду говорить о ней. В том смысле, что реализацию Леонида Эйсымонта не видел.

К Рефалу-2 я читал только руководство и запускал тестовые примеры из дистрибутива. Программировать на нём не пробовал.

Синтаксис Рефала-2 напоминает о временах перфокарт. Одна строка — одно предложение, знак переноса — + в конце строки. Если я ничего не путаю, длина строки ограничена 76 символами (потому что колонки 77–80 использовались для нумерации перфокарт). Или я это путаю с описанием метакода-Б из одного из препринтов Турчина.

Предложения состоят из образца и результата, разделённых знаком равенства. Нет ни условий, ни блоков, ни других вкусностей.

Имена переменных могут состоять из одного символа — буквы или цифры. По мне этого мало, но другие программисты считают, что этого достаточно. Вызов функции записывается как k/F/ аргумент. или <F аргумент>. Есть поддержка сопоставления справа-налево. Имена функций и целые числа нужно обрамлять знаками /, например, /add/, /42/. Типы переменных — S — символ, W — терм (символ или выражение в скобках), E — произвольное выражение, в том числе и пустое, V — непустое произвольное выражение.

Сильная сторона языка — спецификаторы. Можно для переменных указывать множества значений, которые она может принимать, причём можно явно перечислять, можно использовать дополнения до множества, можно записывать объединения множеств. Точный синтаксис не помню, а документацию скачивать лень.

Поддерживается длинная арифметика — числа представлены цепочками макроцифр, макроцифры лежат в диапазоне 0…224−1.

Есть мутабельные структуры данных — глобальная копилка, статические и динамические ящики. Копилка — глобальный ассоциативный массив. Статические ящики — функции-глобальные переменные, при вызове возвращают аргумент предыдущего вызова. Динамические ящики создаются во время выполнения программы, доступны по ссылке. Для динамических ящиков делается сборка мусора.

Есть поддержка вычислений в «песочнице». Функция-песочница принимает ссылку на функцию и её аргумент. Если функция вычислилась успешно, то функция-песочница возвращает код успешности (какое-то число) и её результат. Если функция упала (ошибка отождествления или недостаток памяти) — возвращается код ошибки.

Компилятор формирует исходники на ассемблере (FASM вроде), их затем нужно откомпилировать и слинковать с рантаймом. Компилятор написан на Си, поэтому работает быстро. На самом деле является «полукомпилятором», поскольку исходники ассемблера содержат не машинный код, а определения данных (инструкции db, кто знает ассемблер, поймёт) — интерпретируемый код. А рантайм содержит интерпретатор этого кода.

Есть хорошо документированный интерфейс с языком Си — можно писать встроенные функции/машинные операции (я их предпочитаю называть «нативными функциями»).

Исходники открыты. Поддерживается Windows и, вроде, Linux.

Промежуточное представление объектных выражений — классическое «плоское» списковое. Скобочные термы представлены как «узел-левая скобка» — содержимое скобок — «узел-правая скобка», узлы-скобки содержат указатели друг на друга. А это значит, что если в правой части какая-то W-, V- или E-переменная упоминается больше раз, чем в левой, то дополнительные экземпляры строятся путём копирования. И время копирования пропорционально длине записи значения этих переменных (иначе говоря, количеству узлов списка).

Рефал/2

Независимая реализация Рефала-2, написанная Василием Стеллецким для себя. Я в ней разбирался, поэтому о ней тоже напишу несколько слов.

Является полукомпилятором: состоит из компилятора в язык сборки и интерпретатора языка сборки. Компилятор написан на себе, интерпретатор на Си.

Реализация ориентирована на русскоязычную DOS/Windows, в частности, поддерживаются имена функций и переменных русскими буквами в кодировке 866. Размер поля зрения ограничен 32 килобайтами, что для задач Василия является достаточным (о чём он недавно писал). Спецификаторы переменных очень ограничены — поддерживается или множество символов-литер, или дополнение до этого множества. Вызов функции записывается как k/F/ arg. или к/F/ arg.. Т.е. левую скобку конкретизации можно записывать и кириллицей (в 866).

Уточнение. Размер поля зрения ограничен 32 килозвеньями.

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

Есть порт под Linux.

Исходники открыты, довольно компактны. Мне было достаточно интересно разобраться в этой реализации.

Промежуточное представление данных — классическое плоское списковое.

 

Ещё есть где-то на GitHub’е интерпретатор Рефала-2, написанный какими-то чуваками из МГУ. С чуваками не знаком, ихнюю реализацию не изучал и даже не скачивал, поэтому о ней ничего не напишу.

 

*Лирическое отступление. В ветке дискуссии обсуждалось универсальное AST, в которое можно компилировать разные диалекты Рефала. Если в него компилировать Рефал-2, потребуется как-то изображать спецификаторы переменных.

Рефал-5

На мой взгляд — классическая, «дефолтовая» реализация Рефала. Если говорят о Рефале, то чаще всего подразумевают Рефал-5. Например, Рефал-5 рассматривал Бойко Банчев в статье для последнего номера журнала «Практика функционального программирования», Рефал-5 рассматривался в главе, посвящённой Рефалу в книжке Николая Николаевича Непейводы «Основания программирования». Из всех диалектов Рефала больше всего известны Рефал-5 и Рефал-2 (на сколько я замечал).

В ней используется синтаксис, ставший стандартом де факто в последующих реализациях Рефала — с фигурными скобками для записи функций, с именами переменных вида t.Имя (ранее был допустим вариант и tX — без точки с одной буквой), с точками с запятой в конце предложений, свободной записью программы. Виды переменных тоже классические — s, t, e — символ, терм, произвольное выражение (типа непустых выражений нет).

В начале 2000-х синтаксис Рефала-5 менялся. Запретили имена переменных без точки, ввели регистрозависимость, разрешили имена функций и идентификаторы с маленькой буквы, ввели идентификаторы (составные символы) в двойных кавычках и ряд других деталей. Из-за чего русскоязычный перевод учебника Турчина устарел.

Поддерживаются только три типа символов: символы-литеры, символы-слова (составные символы) и символы-числа (макроцифры). Длинная арифметика такая же, как и в Рефале-2, только основание системы счисления — 232.

В предложениях поддерживаются условия и блоки. Условие — возможность наложить на образец некоторое ограничение в виде сопоставления некоторого выражения со вспомогательным образцом. Блок — вызов безымянной функции в правой части. Предложение с условием оформляется как

P, R1 : P1 = R;

Здесь на образец P наложено условие — R1 : P1. Правая часть предложения R выполняется тогда, когда найдена подстановка переменных в P такая, что результат вычисления R1 может быть сопоставлен с образцом P1. Соответственно, в R1 могут входить переменные из P, в R — переменные из P и P1.

Предложение с блоком имеет вид

P, R : { P1 = R1; P2 = R2; … };

Здесь при успешном сопоставлении с P вычисляется R и выполняется безымянная функция (записанная блоком) с аргументом, который является результатом вычисления R. При этом и в образцы P1, P2… и в результаты R1, R2… могут входить переменные из образца P. Если сопоставление R ни с одним из образцов P1, P2… не оказалось успешным, то программа падает с ошибкой отождествления.

Понятно, что условия и блоки можно комбинировать между собой — у образца может быть несколько условий, оно при этом может оканчиваться на блок, внутри блока могут быть условия и блоки и т.д.

Есть классическая копилка. Статических и динамических ящиков нет.

Язык компактный и лёгкий для понимания из-за свой ограниченности.

(Этот абзац можно пропустить.) Есть средства поддержки метавычислений — функции Mu, Dn, Up, Ev-met и две недокументированные. Mu позволяет вызвать функцию по имени из области видимости записи вызова функции и (недокументировано) среди всех entry-функций программы. Например, <Mu Add 1 2> вызовет <Add 1 2>. Функции Dn, Up и Ev-met работают с метакодом — выражениями языка Рефал общего вида (со скобками активации и переменными), записанными в виде объектных выражений. Dn переводит обычное выражение в метакод. Up берёт закодированное выражение (без переменных) и его вычисляет. Например, <Up ('!' Add ('*' '-' 1) 42)> вычислит выражение <Add ('-' 1) 42>. Ev-met может выполнять выражение в метакоде с переменными, т.е. фактически символьно вычислять некоторое обобщённое выражение + возможности песочницы.

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

Исходная реализация (компилятор и интерпретатор) написаны на Си, работают быстро. Есть реализация компилятора (crefal.rsl), написанная на Рефале, я ей почти не пользовался. Реализация на Рефале умеет оптимизировать вычисление условий для последнего предложения (см. второй том «Сборника трудов по функциональному языку программирования Рефал» под редакцией Андрея Немытых).

Представление объектных выражений — классическое плоское списковое.

Мне кажется, Рефал-5 разрабатывался как язык для написания суперкомпиляторов Рефала и вообще исследований по суперкомпиляции. В частности, функция Ev-met предназначена для ускорения суперкомпиляции и она реально использовалась в SCP3 (в SCP4 не используется). Этим можно объяснить и общую ограниченность библиотеки встроенных функций, и отсутствие мутабельных структур данных, кроме копилки. Но это моё личное мнение.

Рефал Плюс

На Рефале Плюс я не программировал, даже его не скачивал. Зато внимательно читал документацию.

Синтаксис языка развивает синтаксис Рефала-5 (не Рефала-2). Т.е. тоже фигурные скобки, точки с запятой, длинные имена переменных… Но наследует он не (только) от Рефала-5, но и от Рефала-4. От Рефала-5 он взял синтаксис, от Рефала-4 — семантику.

В Рефале-5, если у образца есть условие, была найдена подстановка переменных в образце и условие не выполнилось, то сначала ищется другая подстановка (удлинения открытых переменных), а если таковой нет, то выполнение передаётся на следующее предложение. В Рефале Плюс неуспех в выполнении условия был обобщён в неуспех вообще. В частности, неуспехи теперь могут возвращать и функции как результат своего выполнения, и блоки (записанные как \{ … }), если ни одно предложение не выполнилось. Ещё неуспехи можно усиливать (чтобы они не перехватывались в некоторой части предложения) и ослаблять. Ради управления всеми этими неуспехами придуман сложный синтаксис с такими понятиями как «хвост», «тропа», «источник», «отсечение», «забор», «развилка» и т.д. Очень поэтичные названия конструкций, мне нравятся.

Помимо образцовых блоков (как в Рефале-5), там есть и результатные блоки. Каждое предложение (вернее, тропа) в них начинается не с образцового выражения, а результатного. Эти результатные выражения, как правило, служат условиями — если одно вернуло неуспех, управление передаётся на следующее. Пример:

F sX sY
  , {
      <LT? sX sY> = <Print 'X < Y'>;
      <GT? sX sY> = <Print 'X > Y'>;
      = <Print 'X = Y'>;
    };

Да, если функция состоит из одного предложения, фигурные скобки можно не писать.

И вообще, образцовые блоки формально являются синтаксическим сахаром над результатными блоками.

R : { P1 = R1; P2 = R2; … } ≡ R :: eX, { eX : P1 = R1; eX : P2 = R2; … }

Ещё в Рефале Плюс используется массивное представление — представление с дорогой конкатенацией. А это значит, что если функция получает, скажем, символ, два выражения и терм, то вот так вызывать её накладно: <F s1 e2 (e3) t4> — потребуется выполнить дорогую конкатенацию — построить массив из 1 + |e2| + 1 + 1 элементов и туда скопировать s1, e2, ссылку на скобочный терм и t4.

Что же делать? Можно, конечно, выбирать такой формат <F s1 (e2) (e3) t4> — конкатенация будет, но за константное время. И придётся построить два скобочных терма. Но ради эффективность ещё более усложнили язык (как будто неуспехов мало) — ввели форматы функций. Для каждой функции нужно указывать формат аргумента и формат результата:

$func F s1 e2 (e3) t4 = (e5) e6 (e7);

Теперь при вызове <F s1 e2 (e3) t4> вообще не выполняется ни конкатенаций, ни построений скобочного терма. Для разбора значений функций в язык ввели ещё и присваивания, конструкции вида R :: He, где жёсткое выражение He должно совпадать с форматом выражения R (или обобщать его).

В общем, на первый взгляд, синтаксис сложный. Но, если понять, то всё становится просто и красиво.

Язык Рефал-4 был придуман Сергеем Романенко (не знаю, были ли его реализации, или он остался лишь нотацией) как расширение полного базисного Рефала, на котором выразима прогонка, о чём написал два препринта (они легко находятся). И в нём были и неуспехи, и условия, и результатные блоки (но не было образцовых блоков), заборы с калитками (усиления и ослабления неуспехов). А в препринтах были формулы, по которым можно выполнить прогонку функции на Рефале-4 (к сожалению, формулы неалгоритмизируемые или неалгоритмизированные). Так что синтаксис Рефала Плюс имеет цельный математический фундамент.

Собственно, весь Рефал Плюс вытекает из (а) теоретических основ Рефала-4 и (б) массивного представления данных с дорогой конкатенацией (но дешёвым копированием). Если это понять, то всё красиво и ничего сложного.

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

Реализации Рефала Плюс за последнее время уже несколько раз обсуждали — есть старая на Си в машинный код, есть новая самоприменимая в Си и Java, отсылаю читателей к архиву рассылки.

Арк: Кажется, даже была сделана параллельная реализация через компиляцию в Т-язык. Про неё есть статья Антона и Юры (которую можно найти на странице https://pat.keldysh.ru/~orlov/publications.html). Интересно, жива ли эта реализация и на каких платформах может работать.

Сам язык создаёт ощущение развитого промышленного языка программирования. Жалко только, что не используется.

Рефал-6

На сколько мне известно, Рефал-6 появился в Переславле-Залесском (ИПС РАН) как альтернативная реализация Рефала-5, но потом он попал в руки Аркадия Климова. И Аркадий стал добавлять в язык разные возможности из Рефала Плюс: неуспехи, результатные блоки, управление неуспехами (усиление, ослабление, инверсия). В синтаксис введено понятие действия — оно может пополнять среду переменными, изменять текущее выражение и формировать неуспех. Образцовые действия текущее выражение не изменяют, пополняют среду переменными из образца. Результатные не меняют среду, но меняют текущее выражение.

F {
  e.1 'x' : 'x' e.2 = xx;
  e.2 = <WriteLn 'Hello'> = <WriteLn 'World'>;
}

Первое предложение принимает либо строку 'x', либо строки, которые начинаются и кончаются на 'x'. Во втором предложении идут подряд два результатных действия — возвращаемое значение первого игнорируется.

Арк: Все функции и блоки — откатные, но знак = блокирует откат назад через него (этим и только этим он отличается от запятой). Поэтому, в частности, базисное подмножество (где есть только предложения вида: образец = выражение) работает как раньше, без откатов.

Благодаря понятию действия синтаксис языка существенно проще, чем у Рефала Плюс.

Представление объектных выражений списковое с подвешенными скобками. Каждому терму соответствует отдельное звено списка, в звене скобочного терма находится ссылка на подвыражение. Благодаря чему копирование выражений дешевле, чем в плоском списковом — время создания копии пропорционально не длине записи, а количеству термов. t-переменные копируются за константное время. Но, с другой стороны, иногда бывает неочевидно, когда выражения копируются, а когда переносятся.

Арк: В частности, переноситься (вместо копирования) могут даже подвыражения из-под скобок, если на эту пару скобок больше нигде нет ссылок (и так рекурсивно вглубь выражения!).

На данный момент есть реализация Рефала-6 только для Windows, на unix-like переносить её, вроде, не пробовали (Аркадий Климов поправит).

Арк: Пробовал (еще в 90-х), работала, можно еще раз попробовать.

Тоже полукомпилятор: компилирует в промежуточный код (в виде сериализованных данных в текстовой форме), который затем выполняется интерпретатором. Написан на себе. Когда-то запускал Рефал-5 и Рефал-6 на 486 машине, первый выполнялся мгновенно, второй с заметной задержкой.

Интерпретатор динамичный — в процессе работы может загружать и выгружать модули, можно на лету править код функций (даже отладчик, вроде, написан на Рефале).

Арк: Имеется в виду трассировщик, который подменяет тела функций после их загрузки.

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

Арк: И на каждый мутабельный тип есть немутабельный аналог. Например, ящик — скобочный терм, строка — слово, и т.д. Это все называется «контейнеры», имеющие единый интерфейс доступа к содержимому. В частности символ-число — это контейнер для битовой строки.

Сама модульность с динамическим связыванием имён в runtime через $EXTRN/$ENTRY тоже реализована на уровне Рефала.

Реализация «запечатана» как и Рефал-5 — пользователь ограничен набором встроенных функций, реализованных в интерпретаторе. Однако, исходники доступны и при необходимости можно написать свои встроенные функции.

Арк: Есть функции чтения-записи выражений, READ и WRITE с модификациями, которые по сути выполняют метакодовое преобразование (используя метакод-Б, который воплощен в самом языке). В частности, READ позволяет вычислить закодированное Рефал-выражение без переменных (с вызовами функций в угловых скобках). При этом аккуратно обрабатываются символы-ссылки, согласно заданной таблице имён. Такой ввод-вывод можно осуществить и из/в строки объектных знаков.

Рефал-Java

Диалект Рефала-6 (диалект диалекта). Написан братьями Климовыми (кто-то из них недавно писал, что большую часть кода написал Аркадий).

Синтаксис такой же, как в Рефале-6, имеет только некоторые расширения для совместимости с Java, например, ключевое слово $import, кажется. Компилируется в Java. Представление объектных выражений — массивное.

Арк: Параллельно сохранён и старый режим связывания имён через $EXTRN/$ENTRY – для совместимости с полукомпилирующей реализацией Рефала-6)

Имеет удивительно гладкий и чистый интерфейс с языком Java — объектные выражения представляются массивами Object’ов, если элемент массива — массив Object’ов — то это скобочный терм. Любое другое значение — символ. Функция Рефала компилируется в метод на Java, принимающий и возвращающий массив. Возвращаемое значение null символизирует неуспех.

Рефал-7

Придуман Сергеем Скоробогатовым, наследует от Рефала-5/Рефала-6. О нём мало кто знает, поэтому всё-таки дам ссылку (тем более, что ссылку эту найти непросто):

https://mazdaywik.github.io/direct-link/refal.pdf

Важное нововведение языка — функции высших порядков (вложенные функции), которые могут быть и безымянными (как в Рефале-5λ), так и именованными. Синтаксис из Рефала-6 наследует понятие действия, поддерживает неуспехи, но средства работы с ними беднее. И образцовые, и результатные блоки выкинуты из языка. Результатные выкинуты безвозвратно, образцовые блоки заменяются действием-вызовом функции:

R -> Rt
R => Rt

Здесь Rt — некий результатный терм. Первый вариант прозрачен для неуспехов, второй — нет. Семантику можно описать так:

R -> Rt   ≡   R : e.1,  Rt : t.2,  <t.2 e.1>
R => Rt   ≡   R : e.1 = Rt : t.2 = <t.2 e.1>

В качестве Rt предлагается использовать вложенную функцию, которая является результатным термом:

R -> { P1 = R1; P2 = R2; … }

что делает конструкцию похожей на блок Рефала-5.

Публично доступных реализаций Рефала-7 нет, было несколько реализаций, сделанных совместно со студентами-дипломниками. Но они похоронены на жёстком диске Скоробогатова.

Промежуточное представление данных — векторно-списковое представление — представление с отложенной конкатенацией. О нём, к сожалению, публично доступных материалов тоже нет.

UPD 2019-11-27: есть описание векторно-спискового представления, Скоробогатов разрешил его опубликовать с припиской, что описанное там распараллеливание в экспериментах показало себя плохо.

Рефал-5λ

До себя добрался. Когда-то я, дабы самому понять компиляцию Рефала в императивный код, написал самоприменимый компилятор Рефала с синтаксисом, похожим на Рефал-5. Назвал его Простым Рефалом, поскольку оно действительно был минималистичен (хоть и крив). Этот компилятор порождал исходники на Си++, которые затем компилировались плюсовым компилятором в исполнимые модули. Потом я стал добавлять в него разные возможности, одной из первых были вложенные функции (но только безымянные) из Рефала-7.

Дальше Простой Рефал уже развивался на кафедре — разные фичи (преимущественно, оптимизации) добавляли студенты на курсовых и дипломах. Так в языке появились сначала присваивания (безоткатные условия), а потом уже и нормальные условия. В реализации добавился интерпретируемый код (который отличается от кода Романенко, в худшую сторону).

Потом я подумал, что ещё один несовместимый диалект Рефала никому не нужен и я решил его сделать совместимым с Рефалом-5. Добавил ещё один front-end и переделал библиотеку встроенных функций. Так появился Рефал-5λ.

На текущий момент Рефал-5λ — это точное надмножество Рефала-5 (почти точное, нет поддержки метафункций), в котором есть безоткатные присваивания (добавленные из соображений производительности), вложенные функции, нативные вставки (возможность записать тело функции на Си++), статические ящики и некоторые другие возможности (например, $INCLUDE). Реализация умеет компилировать в файлы интерпретируемого кода, в Си++, умеет несколько оптимизаций, работает на Windows, Linux и macOS, порождает исполнимые файлы. Может компилировать в файлы с интерпретируемым кодом, которые затем можно загрузить динамически (как .dll/.so). Компиляция в .dll/.so операционной системы пока не реализована. Надеюсь, сделаю следующим летом (UPD 2019-11-27: реализовано).

Несмотря на оптимизации и компиляцию в Си++, Рефал-5λ оказывается медленнее, чем обычный Рефал-5 (мерял). То ли из-за того, что сами встроенные функции (включая длинную арифметику) написаны на Рефале, то ли из-за неудачного промежуточного кода.

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

Про два других своих проекта — Модульный Рефал и Рефал-05 — я тут писать уже не буду. И так тут много написано.

Ещё в двух словах. Маратом Исламовым разрабатывался D-Refal — диалект Рефала, наследующий от Рефала-5, но при этом допускающий спецификаторы переменных. Причём для спецификаторов предусматривалась возможность задать чуть ли не грамматику. К сожалению, компилятор и язык не был доведён до конца.

В ИПС РАН когда-то разрабатывался язык FLAC, ориентированный на алгебраические выкладки. По сути он тоже является диалектом Рефала, его реализация напоминает Рефал-6. О FLAC’е написано в одном из томов «Сборника трудов про функциональный язык Рефал» под редакцией Андрея Немытых. Вроде всё.

Если я не упомянул какой-то диалект или реализацию, значит, я о них забыл или не знал. Можете меня спросить о них, что-нибудь о них напишу.

 

А кто дочитал — молодец!

 

С уважением, Александр Коновалов