OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Суббота, 15 Август, 2020 23:58

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 44 ]  На страницу Пред.  1, 2, 3  След.
Автор Сообщение
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Воскресенье, 19 Май, 2019 12:48 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 12:02
Сообщения: 8351
Откуда: Троицк, Москва
Дело не в уровне программиста.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 18:59 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Сергей Оборотов писал(а):
Композиция определяется не языком, а семантикой.

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


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:02 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Info21 писал(а):
Короче, методическая задачка: найти/сделать функ. инструмент, на котором можно было бы "поставить" учням/студням навыки функ. мышления, но при этом остаться в парадигме "единой системы вводных курсов программирования".

По-моему, это было бы более содержательно, чем некоторые иные методические инструменты.

Здесь на форуме относительно недавно указали на доклад насчёт функционального программирования в императивном языке, на плюсах в данном случае:
https://forum.oberoncore.ru/viewtopic.php?f=61&t=5959&start=120#p107724

У автора на страничке (указана в докладе) собраны материалы по проблематике:
https://github.com/graninas/cpp_functional_programming

Там неплохой базис, от иммутабельных структур до монадических конструкций, почва для оценки: от нужно/ненужно до понимания, какие необходимы (и необходимы ли) средства в языке/платформе.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:03 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Не помешает прикидка: нужно/ненужно, как нужно (если вдруг) и реально ли остаться в исходной языковой парадигме. Начать можно с элементарного, однако -- с фундаментального, например, с типовых "монад вида maybe", т.е. композиция действий как поступательный процесс с учётом предшествующих результатов. Попробую в сопоставлении с разными методиками (у меня как раз есть потребность для своих внутренних "расследований", поделюсь некоторыми выдержками).


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:05 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Выше в теме была ссылка на доклад насчёт конкатенативного программирования, ниже нарезка кадров:
https://youtu.be/_IgqJr8jG8M
Вложение:
fct_maybe.png
fct_maybe.png [ 122.69 КБ | Просмотров: 1017 ]

В общем, для Factor в конечном итоге решение можно привести к "монадной" функции, оперирующей массивом (задаётся в "{...}") "квазицитат" (в "[...]", т.е. "лямбдами", в данном примере примитивными).

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


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:10 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Альтернативное императивное решение в виде "текучего интерфейса" (fluent) на плюсах, где на прикладном уровне задаётся политика исполнения процессов, методика передачи аргументов по "потоку" и т.д., исходная постановка:
https://nesteruk.wordpress.com/2015/12/24/maybe-monad-cpp/
Код:
struct Person
{
  Address* address = nullptr;
};

struct Address
{
  string* house_name = nullptr;
};

void print_house_name(Person* p)
{
  if (p!= nullptr && p->address != nullptr && p->address->house_name != nullptr)
    cout << *p->address->house_name << endl;
}

В примитивных случаях повторные вычисления в выражениях вида "p->address ... p->address->house_name" (вычисление смещений и обращение к данным) могут быть оптимизированы компилятором. Проверка валидности насчёт null альтернативно может быть "разрулена" типовыми операторами вида "?." и т.п. (если таковы имеются в языке). Но в общем случае, концептуально, могут быть произвольные (в т.ч. и "тяжёлые") процессы.
В итоге решение сводится к "управляющим" fluent-функциям (через "."):
Код:
void print_house_name(Person* p)
{
  maybe(p)
    .With([](auto x) { return x->address; })
    .With([](auto x) { return x->house_name; })
    .Do([](auto x){ cout << *x << endl; });
}

Если в языке нет "лямбд" по месту, то решение, подобное выше, может быть менее наглядным. В данном случае возникают явные "комбинаторы" (т.е. "."), явные переменные-аргументы. В некоторых языках возможны умолчания вида стандартного аргумента аля "it" для единственного аргумента, или возможны формы для лямбд вида типовых "билдеров" в стиле Kotlin, Groovy и т.п.

Однако в целом, "maybe-монады" выше, фактически, ведь дублируют средства императивного языка, который сам по себе предназначен для явного выражения следования действий. И все эти "монады", по сути-то, якобы лишь создают избыточный уровень в абстракции предметки. Нужен ли он?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:15 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Об императивных "монадных" решениях "в лоб" на этом форуме было немало "горячих" дискуссий, напр.:
Конструкция AND THEN?
Нормализация вложенных if

Основные приёмы борьбы с лесом if-ов и вложенными досрочными return и т.д. сводятся к техникам, которые уже были обозначены на картинках и цитатах выше. Т.е. либо краткая форма вида:
IF <этап0> AND <этап1> AND <этап3> ... THEN....

или "склейка" этапов через проверки, однако в т.ч. избыточные по сути:
https://forum.oberoncore.ru/viewtopic.php?f=6&t=2290

https://forum.oberoncore.ru/viewtopic.php?f=27&t=3175&p=57993&hilit=andif#p57990
Код:
Первый этап;
ok := первый этап удался;
IF ok THEN
  Второй этап;
  ok := второй этап удался
END;
IF ok THEN
  Действия при успехе
ELSE
  Действия при неуспехе
END


В С/С++ возможны дополнительные трюки с оператором следования и замаскированными ленивыми логическими вычислениями:
http://valexey.blogspot.com/2011/01/and-then-in-c.html

Такие варианты компактизации, как ниже:
https://forum.oberoncore.ru/viewtopic.php?f=27&t=3175&p=57993&hilit=andif#p58065
Код:
step := 1; ok := TRUE;
WHILE (step # 3) & ok DO
  CASE step OF
  | 1:
    Первый этап;
    ok := первый этап успешен
  | 2:
    Второй этап;
    ok := второй этап успешен
  END;
  INC(step)
END

по сути-то, искажают исходное: искусственно вводится циклическая форма для последовательного процесса, где каждая итерация содержательно отличается.

А в случаях реально итеративных процессов проблематика усложняется, и, напр., возникает желание в особых формах циклов (даже с учётом некоторой аксиоматизации):
Обобщенный цикл
с уточнением: https://forum.oberoncore.ru/viewtopic.php?f=86&t=3159&start=20#p60161
или:
https://forum.oberoncore.ru/viewtopic.php?f=86&t=3159#p59240
Код:
LOOP
  ... ; ... ;
CHECK Conjunct1 DO
  ...; ... ;
CHECK Conjunct2 DO
  ...; ... ;
END


В связи с последним вспомнилась и любопытная тема с иллюстрацией таких циклов:
Выход из цикла или смерть Кощея


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:20 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
По последней ссылке выше сайт сейчас не пашет (во всяком случае у меня не открывается), но я у себя откопал парочку сохраненных страничек, и процитирую ключевое. Постановка:
Peter Almazov писал(а):
Этот пост адресован тем, кто считает наличие exit/break внутри цикла плохим тоном. В идеале, тем, кто может показать, чем это плохо, но где ж таких возьмешь :)
Те, кто лепит exit/break в теле цикла без малейших колебаний, могут дальше не читать.

Как известно, смерть Кощея находится в игле, игла в яйце, яйцо в утке, утка в зайце, заяц в сундуке. Сундуков у нас много (массив), а задачи рассмотрим две.
1. Найти первую смерть Кощея, т. е., иглу (считаем, что таких сундуков может быть много).
2. Найти первый сундук, в котором нет смерти – там чего-нибудь не хватает.

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

При данных ограничениях мне приходят в голову только решения, использующие exit/break :(
Некрасиво, но что поделаешь…
Вот решения на псевдокоде, неотличимом от C#.

[ ... см. вложения ...]

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

Что предлагается сделать.
Изобрести (или вспомнить где-то реализованную) конструкцию для цикла, которая позволяла бы записать решение без компромиссов, образцово-показательно, эффективно.
ИКР, короче. Идеальный конечный результат.

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

Вложение:
3cb7edb8ee8a.png
3cb7edb8ee8a.png [ 10.07 КБ | Просмотров: 1015 ]

Вложение:
09acc354c0a9.png
09acc354c0a9.png [ 8.83 КБ | Просмотров: 1015 ]


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:23 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
И предлагаемое решение на основе обобщённого цикла:
Peter Almazov писал(а):
Суть проблемы в следующем.
Традиционный цикл while имеет одну точку выхода – логическое выражение в заголовке цикла. В данной задаче (совсем не такой уж редкой, кстати) вычисление условия выхода – многоступенчатый процесс, в котором надо закешировать промежуточные переменные. Поэтому точку выхода надо растянуть до области выхода. В цикле while на Си это можно сделать с некоторой натяжкой, но, это конечно, изврат.

Практическая реализация предлагается такая: добавить ключевое слово АndWhile.
По русски, кстати, тоже будет неплохо: Пока ...ИПока...

Сразу пример, чтобы было понятно.

[ ... см. вложения ...]

Тело цикла (строка "i=i+1") будет выполняться пока истинна коньюнкция всех охран – выражений после while и andwhile. Соответственно, после выхода из цикла будет истинна коньюнкция отрицаний всех охран.

В принципе, слово andwhile годится и для цикла do{….} while. Некошерно, конечно начинать с and когда еще не было просто while, но ничего лучше мне не удалось придумать.

Внедрить это в язык проще пареной репы, т. к. andwhile – это замаскированный if-exit.
Но на практике такое внедрение приведет к грандиозному, эпическому провалу.
Во-первых, andwhile нельзя засовывать в скобочные конструкции (в if, например) внутри цикла. Но это еще можно проверить.
Во-вторых, в области выхода (от первого while до последнего andwhile) нельзя выполнять никаких действий, влияющих на инвариант цикла. Только вычисление условий выхода и кеширование переменных. Как показывает практика, объяснить это не то что "ополченцам", но и легиону программистов совершенно невозможно. А формализовать это и проверить нельзя (не вижу как).
Поэтому все будет переврано и сделано через жопу самыми невероятными способами.

Peter Almazov писал(а):
Peter Almazov писал(а):
Соответственно, после выхода из цикла будет истинна коньюнкция отрицаний всех охран.

Дизъюнкция, конечно же.

Вложение:
fca770120b9b.png
fca770120b9b.png [ 7.53 КБ | Просмотров: 1015 ]

Вложение:
5f904d4b7357.png
5f904d4b7357.png [ 9.03 КБ | Просмотров: 1015 ]


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:26 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Насчёт "изврата" выше:
Peter Almazov писал(а):
Традиционный цикл while имеет одну точку выхода – логическое выражение в заголовке цикла. В данной задаче (совсем не такой уж редкой, кстати) вычисление условия выхода – многоступенчатый процесс, в котором надо закешировать промежуточные переменные. Поэтому точку выхода надо растянуть до области выхода. В цикле while на Си это можно сделать с некоторой натяжкой, но, это конечно, изврат.

, примеры компактизации:
X512 писал(а):
На Си можно избежать дублирования кода пользуясь тем фактом, что оператор присваивания возвращает значение:
Код:
i = 0;
while ((i < колВоСундуков) && (
       !(заяц = ЗАЯЦ(сундуки[i]))
    || !(утка = УТКА(заяц)))
    || !(яйцо = ЯЙЦО(утка))
    || !(игла = ИГЛА(яйцо))
)) ++i;


Илья Ермаков писал(а):
На Обероне я бы сделал, наверное, так:
Код:
PROCEDURE Store (obj: ANYPTR; VAR ptr: ANYPTR): BOOLEAN;
BEGIN
    ptr := obj;
RETURN obj # NIL
END Store;

i := 0;
WHILE (i < сундуки.length) &
    ~( Store(ЗАЯЦ(сундуки[i]), заяц)) & Store(УТКА(заяц(Заяц)), утка)) &
        Store(ЯЙЦО(утка(Утка)), яйцо)) & Store(ИГЛА(яйцо(Яйцо)), игла)) )
DO
    INC(i)
END

Единственное, чтобы передать переменную по VAR-параметру, она должна иметь тип ANYPTR. Отсюда потом каждый раз приведение типа.

В варианте на Обероне возвращаемые значения в процедурах не выделяются (дополнительная подсветка/выделение/подчёркивание возможны в IDE, но такое решение вне языка). В Си-варианте же присваивание (которое не к месту в условных выражениях) традиционно слабо различимо от сопоставления/сравнения.

А в целом, "изврат" -- прежде всего, в нарушении принципа (императивного) command-query separation. Принцип можно ослабить, когда "команда" (процедура с побочными эффектами над состоянием) возвращает результат, но, всё же, за "монады" внутри условного оператора (фактически, как бы внутри "ромбика"-вопроса в блок-схеме) в виде паразитизма на особенностях логических "программистких" операций (что отличает их от "математических") Бертран Мейер, вероятно, забанил бы постыдно (и любопытный вопрос: что бы предложил взамен?). Оправдать такие решения можно "производственной необходимостью" (полагаю, весьма распространенная практика, вынужденная), но сомнительно в контексте методологии, тем более при сопутствующих рассуждениях о доказательном структурном программировании, где в формальной основе "защиты" отделены от "акций" или операторов.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:28 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Здесь на форуме была представлена некая Оберон-вариация в виде языка СЛанг, с единственным оператором цикла, но в разных формах:
Код:
while index in 1..10 loop // цикл пока, 0+
   body 
end
 
loop
   body 
while condition end // цикл пока, 1+
 
loop // вечный цикл
   body
end

Как раз здесь можно "перемещать" while для организации "обобщённого" цикла, как-то так:
https://forum.oberoncore.ru/viewtopic.php?f=86&t=3159&start=20#p60161
Код:
WHILE condition LOOP
   ... 
ANDWHILE condition DO
   ...
ANDWHILE condition DO
   ...
END;


LOOP
   ... 
WHILE condition DO
   ...
ANDWHILE condition DO
   ...
END;


LOOP
   ... 
WHILE condition DO
   ...
ANDWHILE condition DO
   ...
ANDWHILE condition END;

Видимо, рядом возможна и конструкция "ELSIF" для "защит" в "цикле Дейкстры" (чтобы уж удовлетворить все запросы). Плюс секция в блоке как "ELSE" -- исполняется однократно, если изначально ни одно из условий/охран не сработало.

Задача про Кощея выше демонстрирует то, что лишь "обобщённого" цикла недостаточно, в комплекте желателен и нециклический "прогресс", т.е. тогда уж и "AND THEN" в нагрузку:
https://forum.oberoncore.ru/viewtopic.php?f=27&t=3175&p=57993&hilit=andif#p57987
Код:
Первый_этап;
IF первый этап удался THEN
  Второй_этап
ANDIF второй этап удался THEN
  Третий_этап
ANDIF третий этап удался THEN
  Завершающие действия
ELSE
  Задача не выполнена
END

Видимо, в блоке придётся не смешивать ANDIF и ELSIF (как и ANDWHILE с ELSIF).

А в целом, будет ли в результате язык как "упрощённый" Pascal, и оправдывают ли подобные конструкции свою избыточность в полной мере?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:33 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Цитата:
Мне кажется, что можно попробовать программировать в функциональном стиле на Обероне.
Что для этого надо?
- Не использовать глобальные переменные;
- Не использовать процедуры, а использовать функции (вместо процедуры NEW для выделения памяти, нужно вызывать функцию).
- Не использовать возврат значений через VAR, а возвращать все значения через результат функции (а так как, например, записи нельзя возвращать как результат функции, то нужно выделять память под запись и возвращать указатель на запись);

Как минимум, такая функциональная интерпретация не привносит ничего качественно нового для решения задач выше.
Задачи, конечно же, решаются, в том числе и в формах, строго соответствующих канонам структурного программирования, но с соответствующей ценой.

И в контексте функциональной интерпретации желательно не забывать про основную "фишку" функциональщины -- ленивости вычислений. В императиве затруднительно на практике протаскивать "ленивые" аргументы (лямбды) по функциям:
http://pchiusano.blogspot.com/2009/05/optional-laziness-doesnt-quite-cut-it.html
http://pchiusano.blogspot.com/2009/06/perfect-strictness-analysis-part-1.html
http://pchiusano.blogspot.com/2009/06/perfect-strictness-analysis-part-2.html

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

Альтернативное решение -- системы уравнений в стиле Lustre/Lucid Synchrone (на форуме рядом есть материалы), где "строгая" (не "ленивая") семантика, и используется т.н. clock calculus для детерминирования наличия/отсутствия данных (в целом -- для полной верификации).
Данные из сундука можно было бы извлекать гипотетически как-то так, в Pascal-интерпретации:
Код:
procedure добыть_иглу(сундук: Cундук on u) = (игла: Игла on v);
var
  ...
let
   заяц := ЗАЯЦ(сундук);
   утка := УТКА(заяц);
   яйцо := ЯЙЦО(утка);
   игла := ИГЛА(яйцо);
tel   

Выше для входного и выходного аргумента через оператор "on" явно (также компилятор может много чего выводить автоматически) указаны т.н. clock -- виртуальные такты исполнения, обозначенные в виде переменных "u" и "v". Переменные различны, что означает: выход "игла" не всегда соответствует входу "сундук", т.е. выход может "отсутствовать". Пусть процедуры ЗАЯЦ, УТКА и т.д. определены аналогично, тогда если "ЗАЯЦ(сундук)" не возвращает данных (переменной "заяц" как бы нет), то уравнение "УТКА(заяц)" не вычисляется/не исполняется и т.д. В типовой распространённой функциональщине обычно в подобных случаях результаты требуется "запаковывать" в алгебраические типы вида "Option = Some(x)|None" и т.п., и затем "распаковывать" через pattern matching (алгебраические типы применимы и в Lustre), или же создавать специализированные типы для "монад" и т.д.

В общем, альтернатива традиционным функциональным уравнениям имеется (не беспочвенная), которую можно прикрутить и к Pascal-подобному языку (как в Lucid Synchrone -- ML с уравнениями Lustre). Некие "let"-процедуры по сути затем трансформируются в обычные императивные "begin ... end"-процедуры, с лесом if-в и т.д. (с возможным (или нет) дополнительным кодированием состояния).
Форма алгоритмики в виде системы уравнений может оказаться гораздо практичнее традиционного императива в случаях сложных условий, или в целом при реализации политики "управления по данным" (с поддержкой диффуров возникает потенциал и для имитационного моделирования).


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:36 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Однако, функциональные формы в виде системы уравнений имеют и недостаток. Ниже пример функции на Clean (Haskell-подобный язык, один из его предков):
https://clean.cs.ru.nl/download/html_report/CleanRep.2.2_5.htm#_Toc311798006
Код:
readchars:: *File -> ([Char], *File)
readchars file
| not ok     = ([],file1)
| otherwise  = ([char:chars], file2)
where
    (ok,char,file1)   = freadc file
    (chars,file2)     = readchars file1

Типы со звёздочкой вида "*File" есть уникальные или линейные в традиционном смысле, т.е. с "эксклюзивным" доступом. В данном случае исходная переменная file (входной аргумент) "протягивается" в виде переменных file, file1, file2, т.е. возникает "протаскивание" состояния по операциям в виде новых фаз -- изменение детали/заготовки на производственном конвейере. Однако, следование операций по преобразованию детали не вырисовывается в явном виде, необходимо дополнительно восстанавливать на основе определений этих переменных, что не всегда удобно. Для явного намерения декларировать следование процессов в Clean используются т.н. Let-Before Expression вместо классической "do-нотации" (синтаксическая обвёртка над монадами). Через "#" задаются упреждающие формулы (let-before), которые могут быть определены перед "телом" функции (перед основным "="), перед защитным выражением (в некотором смысле, конструкция схожа с популярным "let ... in ..."):
Код:
readchars:: *File -> ([Char], *File)
readchars file
#   (ok,char,file1)   = freadc file
|   not ok            = ([],file1)
#   (chars, file2)    = readchars file1
=   ([char:chars], file2)

Выше имена переменных сохранены как file, file1, file2, но можно использовать единственное имя переменной (мол mutable):
Код:
readchars:: *File -> ([Char], *File)
readchars file
#   (ok,char,file)    = freadc file
|   not ok            = ([],file)
#   (chars,file)      = readchars file
=   ([char:chars], file)

Лишние "решётки" можно опустить:
Код:
exchange:: (a, b) -> (b, a)
exchange (x, y)
#   temp = x
    x    = y
    y    = temp
=   (x, y)

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


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:40 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Подобный функциональный императив можно гипотетически попробовать прикрутить и к Pascal-подобному языку.

Вспоминается диалект Паскаля -- MSElang (на базе проекта MSEide+MSEgui), где в качестве финального return в функциях предлагается особый вариант оператора ":=" (как "=" для "тела" функции в Clean выше):
https://gitlab.com/mseide-msegui/mselang/wikis/home
Код:
procedure abcfunc(par1: int32, par2: int32): int32;
begin
 if par2 > 10 then
  return 42;
 end;
 abcproc(1,2);
 := 123;
end;

В дополнение напрашиваются и "защитные" выражения, нечто вроде (по мотивам readchars выше):
Код:
function readchars(file: File): (string, File);
var
  ok: boolean;
  c: char;
  chars: string;
begin
  (ok, c, file) := freadc(file);
  | not ok      := ('', file);
  (chars, file) := readchars(file);
  := (append(chars, c), file);
end; 

Возврат (выход) результата функции (как ":=" или return) возможен либо в конце, либо в "защитном" выражении, где statement начинается с "|" (видимо, желательно, чтобы операторы вида case..of и др. не имели форму с использованием "|"). Причём return возможен только в блоке верхнего уровня (не внутри операторных блоков, т.е. условий, циклов). Для вложенных процессов (с потребностью "защит") необходимо использовать иные функции (в т.ч. и локальные), при этом гранулярность алгоритмов всё же больше, чем, напр., при использовании конструкций вида (единичные действия как процедуры):
IF <этап0> AND <этап1> AND <этап3> ... THEN....

Насчёт "протягивания" состояния через функции.
В идеале линейные типы должны быть в языке из коробки, как в том же Clean. И компилятор должен отслеживать валидность переменных, как, напр., сегодня делает компилятор Rust-а (если тип не задекларирован как value с копированием, то используется т.н. move-семантика).

Возможна (необязательно) форма функций без изменяемых параметров (т.е. var/out-параметры в Pascal-стиле трансформируются во вход (если есть) и выход, с возможностью результата-кортежа, деструктивно разбираемого по переменным, процедуры без явного результата подразумевают тип-результат unit или void). Из Lustre заимствуются "регионы" (как в Rust, но проще модель), плюс некоторые послабления:
Код:
(* Через оператор "at" задаётся "регион" в памяти, идентифицируемый через переменную.
   Здесь входной x и результат функции являются ссылками на один и тот же
   "регион" -- т.е. входной х является изменяемый *)
function inc(x at r: integer) = (integer at r);
...
(* использование: *)
n := inc(n);

(* или без сохранения результата в классическом императивном стиле: *)
inc(n);

Решение для "защит" выше, всё же, не такое спорное, как, напр., в Julia, где в языке для математиков методологически предлагают паразитизм на short-circuit evaluation логических операторов:
https://docs.julialang.org/en/v1/manual/control-flow/#Short-Circuit-Evaluation-1
Код:
function fact(n::Int)
    n >= 0 || error("n must be non-negative")
    n == 0 && return 1
    n * fact(n-1)
end


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:48 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Техника "защит" выше требует дублирования функционала в случае одного и того же события (возникающего в разных местах) и действий-обработчика. В автоматных методиках для ликвидации избыточности применяют иерархическую композицию процессов (автоматов), где реакции в "супер"-состоянии (комплексном) приводят к переходам из этого состояния с "вытеснением" (прекращением работы) вложенных состояний. Вновь к опыту древних Lustre/Esterel.

В императивном Esterel операции "вытеснения" (эмулирующие иерархию автоматов) гибкие, но несколько усложнённые. На примере ForeC -- расширение Си некоторыми Esterel-конструкциями:
Вложение:
ForeC_abort.png
ForeC_abort.png [ 67.16 КБ | Просмотров: 1014 ]

Полная форма оператора abort (прекращает исполнение вложенных операторов, если операторы завершают свою работу "самостоятельно" -- "вытеснения" нет, программа исполняется дальше) образует 4 варианта:
"[weak] abort <block> when [immediate] <condition>",
где "weak abort" -- "слабое" вытеснение -- срабатывание (при подтверждении защитного выражения) в конце такта автомата, без weak -- "сильное" -- сразу же по возможности (в начале такта). "when immediate" -- проверять условие сразу, когда возможно (в начале такта), без immediate -- в конце такта.
Оператор "par" -- исполнение параллельных процессов (параллельная композиция автоматов), "pause" -- аналог типового yield (конец такта автомата).
Разделяемая переменная ("shared") в данном случае окончательно "собирается" через функцию plus после отработки тактов процессов.

В декларативном Lustre есть некое упрощение и используются два оператора until и unless для слабого и сильного "вытеснения".
Для, например, Активного Оберона применим лишь вариант сильного "вытеснения" (как таковые такты автомата не выделяются). Гипотетически можно дополнить секцию finally неким catch или unless (catch обычно воспринимается как конструкция, связанная с исключениями) примерно в таком стиле:
Код:
procedure readchars(file: file);
var
  ok: boolean;
  c: char;
begin
  ok, c := freadc(file);
  use_char1(c);
  ...
  ok, c := freadc(file);
  use_char2(c);
  ...
unless not ok do   //"вытеснение" и обработка
  invalide_read();
  ...
unless c = ZERO do
  invalide_char(c);
  ...
end; 

В отличие от механизма исключений здесь нет никаких вылетов exception/trap за пределы процедуры (обязательства по постусловиям сохраняются). В отличие от, например, механизма defer в Go и плюс предлагаемый handle , scope(...) в D в данном случае нет разрывов в следовании предметных действий, т.е. нет вставок "отложенных" действий (у каждого варианта имеются плюсы и недостатки).
При наличии "вытеснения" (которое ещё необходимо согласовать с операторами циклов -- отдельный вопрос) можно ограничиться "защитой" лишь в начале процедуры на основе входа. Такие конструкции заменяют потребность в "AND THEN" и "AND WHILE" ранее.

К слову, где-то здесь на форуме была ссылка на статейку насчёт того, что техника единственного return только в конце часто является источником ветвлений в алгоритме:
https://blog.timoxley.com/post/47041269194/avoid-else-return-early#_=_


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Вторник, 28 Май, 2019 19:49 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
В общем, вот такие формы "maybe-монад", распространенные и не очень. Буду признателен за указания на ещё "правильные" варианты.

И насчёт Активного Оберона. По публикациям наблюдается не мало всяких концептов. Всё же, какие-то "вытеснения" в нём имеются или нет? (кроме перегрузки после trap. А в целом, ситуация с концептами неоднозначная, к примеру, в диссере про Lock-free многопоточку и FINALLY предлагается забанить).


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Среда, 29 Май, 2019 13:21 

Зарегистрирован: Воскресенье, 28 Май, 2006 22:12
Сообщения: 1470
PSV100 писал(а):
В общем, вот такие формы "maybe-монад", распространенные и не очень. Буду признателен за указания на ещё "правильные" варианты.

И насчёт Активного Оберона. По публикациям наблюдается не мало всяких концептов. Всё же, какие-то "вытеснения" в нём имеются или нет? (кроме перегрузки после trap. А в целом, ситуация с концептами неоднозначная, к примеру, в диссере про Lock-free многопоточку и FINALLY предлагается забанить).

Прочитал два раза весь "корпус" ваших последних N сообщений с иллюстративными примерами.
Вообще не понял, о чём душевная боль.
Может вы пытаетесь найти общее, там где его не стОит искать и применяете подходы с чужой "внутренней логикой работы"?
Нет ли большего смысла отображать (в императивных языках) "постадийность прохождения" именно постадийной проверкой (для случая "однопотоковых" задач) и переходами между состояниями (параллельно работающих автоматов в "многопотоковых" задачах)?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Среда, 29 Май, 2019 16:26 

Зарегистрирован: Пятница, 24 Апрель, 2009 16:28
Сообщения: 548
Откуда: Москва
PSV100 писал(а):
По последней ссылке выше сайт сейчас не пашет (во всяком случае у меня не открывается)
Там сейчас домен http://oberspace.org


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Четверг, 30 Май, 2019 18:50 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Peter Almazov писал(а):
PSV100 писал(а):
По последней ссылке выше сайт сейчас не пашет (во всяком случае у меня не открывается)
Там сейчас домен http://oberspace.org

Спасибо.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Конкатенативный язык Factor
СообщениеДобавлено: Четверг, 30 Май, 2019 19:00 

Зарегистрирован: Понедельник, 25 Июнь, 2012 17:26
Сообщения: 436
Wlad писал(а):
Прочитал два раза весь "корпус" ваших последних N сообщений с иллюстративными примерами.
Вообще не понял, о чём душевная боль.

Боли никакой нет, скорее, наоборот -- в реальной практике на императиве есть "расслабуха" (однако, не всегда) из-за игнорирования или пренебрежения некоторыми положениями структурного программирования, в том числе и там, где в языке даже введён цикл Дейкстры.
Вот свежий пример здесь на форуме:
https://forum.oberoncore.ru/viewtopic.php?f=22&t=6402#p108074
Код:
...
(** Usage: ConsoleUtils.ReplaceStringInFile --old="old string" --new="new string" <Input file> [<Ouput file>] *)
PROCEDURE ReplaceStringInFile*(context: Commands.Context);
VAR
   bOptionsOk, bIgnoreCase: BOOLEAN;
   options: Options.Options;
   aOldPat, aNewPat: ARRAY 256 OF CHAR;
   aFileNameIn, aFileNameOut: Files.FileName;
   strContent: String;
   buffer: Strings.Buffer;
   nRead, nReplaced: LONGINT;
BEGIN
   NEW(options);
   options.Add("o", "old", Options.String);
   options.Add("n", "new", Options.String);
   options.Add("s", "silent", Options.Flag);
   options.Add("c", "case", Options.Flag); (* case sensitive *)
   bOptionsOk := options.Parse(context.arg, context.out) &
      options.GetString("old", aOldPat) & options.GetString("new", aNewPat);
   IF bOptionsOk THEN
      context.arg.SkipWhitespace();
      context.arg.String(aFileNameIn);
      bOptionsOk := aFileNameIn # ""
   END;
   
   bSilent := options.GetFlag("silent");
   bIgnoreCase := ~options.GetFlag("case");

   IF ~bOptionsOk THEN
      Message(context.out, CRLF + "Required parameters not provided. Usage:" + CRLF +
         'ConsoleUtils.ReplaceStringInFile --old="old string" --new="new string" <Input file> [<Ouput file>]' + CRLF)
   ELSE
      context.arg.SkipWhitespace();
      context.arg.String(aFileNameOut);
      strContent := ReadFile(context.out, aFileNameIn, nRead);
      IF strContent # NIL THEN
         Message(context.out, 'File "' + aFileNameIn + '" opened, ' + nRead + ' bytes read.' + CRLF);
         buffer := StringReplace(strContent^, aOldPat, aNewPat, bIgnoreCase, nReplaced);
         Message(context.out, nReplaced + ' occurences of pattern "' + aOldPat + '" found.' + CRLF);
         IF aFileNameOut # "" THEN
            WriteFile(context.out, aFileNameOut, buffer.GetString()^, 0, buffer.GetLength() - 1)
         ELSE
            WriteFile(context.out, aFileNameIn, buffer.GetString()^, 0, buffer.GetLength() - 1)
         END
      END
   END
END ReplaceStringInFile;
...

Выше есть "склейка" этапов через "защиту" с переменной "bOptionsOk". Можит быть частично защита есть избыточная, но в результате меньше вложенности if-ов, без досрочных return и т.д., плюс явно вырисовывается линейное следование этапов (видимо, установка переменных bSilent и bIgnoreCase также должна быть под защитой в зависимости от результатов операции options.Parse(...) с целью явно выразить предполагаемые намерения (да и меньше действий при неуспехе защиты), даже если options.GetFlag в любом случае возвращает какой-то логический результат. Т.е. возможна некая перестройка алгоритма, в результате может оказаться избыточность проверок по bOptionsOk. Но не принципиально, без буквоедства, рассматрение по сути лишь в общих чертах).
И наблюдается традиционная "монадная" and-склейка вида:
bOptionsOk := options.Parse(context.arg, context.out) &
options.GetString("old", aOldPat) & options.GetString("new", aNewPat);

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

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

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


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 44 ]  На страницу Пред.  1, 2, 3  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2020, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB