Добрый день, Александр! «Что касается блоков и, соответственно, области видимости переменных, то блоки я как раз пока что не принял. Мне кажется, что они сильно усложняют чтение и понимание чужих программ.»
Многое зависит от форматирования кода. Я встречал программы с блоками и условиями, которые из-за форматирования разбирать трудно — приходится буквально распутывать структуру кода. И встречал программы, где, напротив, код читается легко, т.к. структура из-за форматирования очевидна. Синтаксис Рефала-2 в этом смысле лучше — её сложнее запутать кривым форматированием. Синтаксис языка ограничивает возможности запутать. Первое предложение функции должно начинаться с имени функции, остальные — с пробела. Длинные предложения разбиваются явным знаком переноса «+» в конце строки. Так что всё равно поймёшь, где предложение начинается, а где кончается. К тому же из-за коротких имён переменных сами предложения зачастую оказываются короткими. «Вообще синтаксической выделение авторекурсии возникло при копировании функций. Если функция сложная, требуются некоторые дополнительные усилия, чтобы „вытравить“ из неё использование своего же имени во избежание трудновылавливаемых ошибок.» Я подумывал о ключевом слове $REC в связи с необходимостью переименования функции при рефакторинге. Но потом освоил Vim, в котором удобно аккуратно переименовывать все вхождения имени (последовательность нажатий клавиш: *cwНовоеИмя<Escape>n.n.n.n). К тому же Vim умеет автодополнять имена в исходном файле на любом языке программирования, в том числе и на Рефале. Благодаря автодополнению вводить одно и то же имя в нескольких рекурсивных вызовах становится довольно просто. Теперь я начинаю понимать потребность и смысл кавер-функций и авторекурсии. Предметная область Вашего диалекта Рефала подразумевает написание шаблонного кода — когда новый код часто строится путём копирования стандартного шаблона и внесения в него изменений, характерных для задачи. Реализация не поддерживает средства, позволяющие вынести общую логику в библиотеку/фреймворк, либо хотя бы разнести функции по разным пространствам имён (в противном случае можно было скопировать файл, локальные функции переименовывать не потребовалось бы, достаточно переименовать entry-функции). И тогда роль группировки различных функций в одну сущность берёт на себя сама функция — отдельные функции отображаются в группы предложений-клозы, а для вызова самой себя используется синтаксический сахар. Сами клозы, как я понимаю, копировать и переименовывать приходится редко. Я предпочитаю избегать копирование кода. Если нужно напечатать две похожие строчки, то я лучше напечатаю вторую строчку вручную, чем скопирую и поправлю. По своему опыту, когда я копирую кусок кода, то позже обнаруживаю ошибку, которая оказывается в обеих копиях и обе приходится править. С уважением, Александр Коновалов From: Александр Гусевgusev_aleksandr_AT_mail.ru Sent: Friday, March 12, 2021 10:52 AM To: refal@botik.ru Subject: Re[2]: Изобретение велосипеда Добрый день, Александр! Спасибо за реакцию. По моему мнению важным аспектом развития языка является лёгкость вхождения и достаточная лёгкость чтения чужих программ. Поэтому я активно интересуюсь такими вроде незначительными деталями реализации как внешний вид программы и форматирование её текста. Интересно понять, что конкретно имелось ввиду, потому что «вложенная функция» — понятие растяжимое. В языках программирования, где вложенные функции есть (как функции высших порядков в функциональных языках или как вложенные процедуры в классическом Паскале), они характеризуются следующими свойствами: синтаксически они расположены внутри определения другой функции, область видимости ограничена той функцией, где они определены, и вложенные функции могут видеть локальные переменные окружающей функции. Я понимаю под вложенными функциями то же самое. Что касается блоков и, соответственно, области видимости переменных, то блоки я как раз пока что не принял. Мне кажется, что они сильно усложняют чтение и понимание чужих программ. Мой прежний практический опыт использования Рефала был основан на Рефале-2 на БЭСМ-6 как раз со старой нотацией скобок и отсутствием блоков. Эти программы легко читались несмотря на несколько архаичный по теперешним меркам ситаксис. Это прямо как по старо-славянски читать. Но понятно притом. )) Вообще, на сколько я понимаю, детерминативы введены отчасти ради эффективной реализации. В этом случае при вычислении конкретизации будут просматриваться не все предложения программы, а только несколько. Предлагаемая реализация (т.е. клозы внутри нормальной кавер-функции) позволяет выполнить ещё одну оптимизацию поиска вариантов при выполнении функции. И при этом не производится никаких дополнительных изменений (усложнений) в синтаксисе. Что касается формата скобок при авторекурсии, то я рассматривал разные варианты, начиная со специального имени $SELF и, конечно, с разными символами. И смотрел, насколько читаем полученный текст. Как раз сдваивание «<<» хорошо выделяется, но глаз не «спотыкается» на таком фрагменте, что хорошо. Поскольку кодировка теперь UTF-8, то ассортимент допустимых символов принципиально расширился (и я их использую в режиме просмотра текста), но набирать их на клавиатуре затруднительно, поэтому от разной «экзотики» я отказался. Время покажет насколько всё выбрано удачно. Вообще синтаксической выделение авторекурсии возникло при копировании функций. Если функция сложная, требуются некоторые дополнительные усилия, чтобы «вытравить» из неё использование своего же имени во избежании трудновылавливаемых ошибок. Спасибо за ссылки, это интересно. Пятница, 12 марта 2021, 0:24 +03:00 от Александр Коновалов a.v.konovalov87_AT_mail.ru <refal@botik.ru>: Добрый вечер, Александр! «Читая как-то информацию о Рефале в сети, наткнулся на упоминание отсутствия вложенных функций как недостатка языка.» Интересно понять, что конкретно имелось ввиду, потому что «вложенная функция» — понятие растяжимое. В языках программирования, где вложенные функции есть (как функции высших порядков в функциональных языках или как вложенные процедуры в классическом Паскале), они характеризуются следующими свойствами: синтаксически они расположены внутри определения другой функции, область видимости ограничена той функцией, где они определены, и вложенные функции могут видеть локальные переменные окружающей функции. Вариант с клозами реализует только первые два свойства. Классический Рефал-5 содержит блоки — конструкции вида , Res : { Sent; Sent; Sent; } Их вполне можно считать вложенными функциями, хоть они и вызываются только один раз. Они действительно синтаксически находятся внутри другой функции, недоступны извне и, главное, в них доступны связанные снаружи переменные. Max { s.X s.Y , <Sub s.X s.Y> : { '−' s.d = s.Y; s.d = s.X; }; } Вложенные функции как функции высших порядков предложены Сергеем Скоробогатовым в Рефале-7: Скоробогатов С. Ю., Чеповский А. М. Разработка нового диалекта языка Refal // Информационные технологии. 2006. 9. C. 31-38. https://bmstu-iu9.github.io/Skorobogatov-Refal-7.pdf Ограниченная поддержка вложенных функций высшего порядка есть в Рефале-5λ — доступны только безымянные вложенные функции. «Под клозом понимается группа строк паттерна, начинающаяся с одного и того же символа, предпочтительно идентификатора. При вызове функции с указанием конкретного идентификатора, набор обрабатываемых паттернов сократится до строк, начинающихся с этого идентификатора.» На самом деле всё наоборот. Если почитать первый препринт Турчина о Рефале: Программирование на языке рефал. I. Неформальное введение в программирование на языке рефал. (keldysh.ru) то можно узнать следующее. Изначально, программа на Рефале была просто набором предложений, а функциональные скобки (скобки конкретизации) были «безымянными». Левая обозначалась к, правая — . (точка). (Знаки < и > Турчин стал использовать уже в Америке, там их ему на конференции предложил МакКарти.) Уже после определения скобок конкретизации на странице 35 препринта (по ссылке выше) Турчин вводит понятие детерминатива — символ после левой скобки конкретизации. А группу предложений с общим детерминативом — как функцию. Вообще, на сколько я понимаю, детерминативы введены отчасти ради эффективной реализации. В этом случае при вычислении конкретизации будут просматриваться не все предложения программы, а только несколько. Вводить кавер-функцию с несколькими подфункциями-клозами можно, такой стиль программирования на Рефале существует. Правда, иногда отдельные клозы не именуют, а просто пользуются различными непересекающимися (или что хуже, пересекающимися) форматами. Такой стиль опасен тем, что ошибившись при вызове, можно случайно вызвать не ту «подфункцию» функции. «„Авторекурсия“ — это вызов функцией самой себя, синтаксически выделяемой как „<<“ вместо „<имя_функции“. Никаких других „вольностей“ в синтаксисе тут нет.» На мой вкус, знак «<< …>» неудачный. Глаз привыкает, что знаки < и > являются парными скобками и всегда должны быть сбалансированы, их число должно быть одинаково. Я бы предпочёл какой-нибудь другой, выделяющийся знак для этой цели, например «<@ …>». Сам я когда-то подумывал о слове $REC для аналогичной цели, но сначала поленился, а потом забил — будет потерян один удобный приём отладки (см. здесь пример отладки DoFib). С уважением, Александр Коновалов From: Александр Гусевgusev_aleksandr_AT_mail.ru Sent: Thursday, March 11, 2021 1:52 PM To: refal@botik.ru Subject: Изобретение велосипеда Добрый день, коллеги! Поделюсь мыслью, показавшейся мне интересной. Читая как-то информацию о Рефале в сети, наткнулся на упоминание отсутствия вложенных функций как недостатка языка. По ходу реализации оказалось, что это не вполне соответствует действительности и такой механизм есть, хотя он не вполне явный и требует некоторой доработки. Я добавлю здесь небольшой текст из своего описания. Возможно, это будет интересно ещё кому-то. Позволил себе поупражняться в изобретении наименований, если что-то уже было сделано на этот счёт до меня, но я не знаю об этом. «Авторекурсия» — это вызов функцией самой себя, синтаксически выделяемой как «<<» вместо «<имя_функции». Никаких других «вольностей» в синтаксисе тут нет. Отрывок: При комбинации авторекурсии и идентификаторов добавляется удобное качество: локальные подфункции внутри одной функции, именуемые клозами. Под клозом понимается группа строк паттерна, начинающаяся с одного и того же символа, предпочтительно идентификатора. При вызове функции с указанием конкретного идентификатора, набор обрабатываемых паттернов сократится до строк, начинающихся с этого идентификатора. Если каждый паттерн начинается с идентификатора, то функция будет натуральной кавер — функцией (НКФ, natural cover) с гарантированной ясной логикой использования клозов. Пример печати квадратов цепочки макроцифр: Cover { Square s.1 = <Mul s.1 s.1>; Line = <Prout 'Конец списка'>; Line e.1 = <Prout '=' e.1>; = <<Line>; s.1 = <<Line <<Square s.1>>; s.1 e.1 = << s.1> << e.1>; }; То же, но в «классическом» варианте: Square { s.1 = <Mul s.1 s.1>; }; Line { = <Prout 'Конец списка'>; e.1 = <Prout '=' e.1>; }; Cover { = <Line>; s.1 = <Line <Square s.1>>; s.1 e.1 = <Cover s.1> <Cover e.1>; }; НКФ можно даже оптимизировать на уровне выполнения — сократить перебор вариантов без участия программиста на Рефале. Это вопрос на перспективу. С уважением, Александр Гусев wlmailhtml:/compose?To=gusev_aleksa...@mail.ru С уважением, Александр Гусев gusev_aleksa...@mail.ru