При разработке своего собственного языка программирования автор должен определиться, какими свойствами его язык должен обладать, ведь в конечном итоге именно это и будет определять назначение языка и особенности его использования. Причем некоторые свойства настолько сильно влияют на итоговую концепцию языка и его синтаксис, что впоследствии их изменение невозможно в принципе, либо новый синтаксис получается очень запутанным и непонятным. Наверно из-за этого многие свойства языков программирования рассматриваются и изучаются как взаимоисключающие.
До недавнего времени я считал, что императивная и декларативная парадигмы написания программ являются антагонистами и взаимоисключающими понятиями. Ведь выбор стиля написания кода, изначально определяется автором языка еще на стадии его проектирования и оказывает влияние на все последующие аспекты.
Но сейчас я думаю, что это не верно. Императивный и декларативный стили программирования не являются взаимоисключающими, а писать программы только в одной из указанных парадигм подталкивают правила синтаксиса, реализованные в языке!
Другими словами, использовать только императивную или только декларативную парадигмы разработчик вынужден не потому, что он не умеет или не хочет писать код по другому, а из-за того, что все более-менее мейнстримные языки программирования ориентированы на использование только одной парадигмы. И выбор только одной парадигмы написания кода, это ограничение, которое навязано программистам создателями языка. Ведь если они изначально ориентировались только на одну концепцию разработки кода, то и разрабатывали синтаксис языка в соответствии с этой парадигмой.
Данная статья — размышление о совместимости декларативной и императивной парадигм программирования и возможности их одновременного использования в рамках одного языка программирования одновременно.
Что такое «Декларативное программирование»?
Вначале мне захотелось разобраться, а существуют ли чисто декларативные языки программирования? Такие, которые могут полностью обходятся без императивных конструкций?
Но чуть не споткнулся уже в самом начале, т.к. даже само определение «декларативное программировании», в вики описывается следующим образом:
Декларати́вное программи́рование — парадигма программирования, в которой задаётся спецификация решения задачи, то есть описывается, что представляет собой проблема и ожидаемый результат. Противоположностью декларативного является императивное программирование, описывающее на том или ином уровне детализации, как решить задачу и представить результат.
Уже в изначальном определении закладывается противопоставление парадигм, а дальше идут рассуждения о «направлении» программирования. И хоть вики сама и не является авторитетным источником информации, но даже такая заготовка статьи смогла показать направление поиска ответов на первый поставленный вопрос.
И заставил задуматься один абзац в конце статьи следующего содержания:
«Чисто декларативные» компьютерные языки зачастую неполны по Тьюрингу — так как теоретически не всегда возможно порождение исполняемого кода по декларативному описанию. Это иногда приводит к спорам о корректности термина «декларативное программирование» (менее спорным является «декларативное описание решения» или, что то же самое, «декларативное описание задачи»).
Судя по наличию такой оговорки, ставится под сомнение наличие «чисто декларативных» языков. И как только пришлось задуматься над это мыслью, то сразу стало очевидным, что какой бы декларативный язык программирования не существовал, то в нем должна быть как минимум одна императивная конструкция, которая будет запускать поиск решения! Ведь любой синоним слова «выполнить» или «запустить» по определению будет императивом!
Например в языке Prolog, который обычно и приводят в качестве примера декларативного языка программирования, «замаскированным» императивным оператором является знак вопроса. Он кажется естественным и логичным, как же без него? Но по своей сути, это императивная конструкция!
Тоже самое касается и языка SQL. Он тоже как бы декларативный, но неожиданно все его команды являются императивными по сути. SELECT, CREATE, ALTER, DROP, INSERT, UPDATE, DELETE и т. д., а декларативными являются только описания условий их выполнения!
В результате, у меня так и не получилось найти чисто-декларативный язык программирования без императивных операторов (может быть, Вики права и такого языка программирования и вовсе не существует?).
А что отличает декларативные языки программирования от императивных?
Согласно определению, декларативными языками программирования их называют из-за возможности записать некие условия в декларативном стиле. Но кроме этого, в декларативных языках как правило присутствует и некий внутренний механизм поиска решений. Подобный «вычислитель» есть и в SQL и Prolog и во многих других декларативных языках.
Интересно, а является ли внутренняя система поиска решений обязательным признаком декларативного стиля программирования или это только особенность конкретного языка программирования, которая не зависит от декларативного стиля написания кода?
Чтобы проверить данные предположения, можно было попробовать написать классическую декларативную программу на императивном языке, у которого отсутствует внутренний механизм поиска решений.
А заодно, таким образом можно будет и изучить вопрос, получится ли написать программу в декларативном стиле используя обычный императивный язык программирования?
Для примера, решил попробовать повторить уже классическую программу в декларативном стиле на Прологе.
parent("Tom","Jake").
parent("Janna","Jake").
parent("Tom","Tim").
male("Tom").
male("Tim").
male("Jake").
female("Janna").
brother(X,Y):-parent(Z,X),parent(Z,Y),male(X),male(Y),X\=Y.
? brother
Получилась вот такая функционально — эквивалентная программа на С++, которая максимально приближена по стилю к декларативному прототипу:
enum sex_ {
male,
female
};
struct human;
typedef std::vector<human *> parent_;
struct human {
const char *name;
sex_ sex;
parent_ parent;
};
human Tom{"Tom", male,{}};
human Janna{"Janna", female,{}};
human Jake{"Jake", male,{&Tom, &Janna}};
human Tim{"Tim", male,{&Tom}};
std::vector<human *> humans{&Tom, &Janna, &Jake, &Tim};
auto brothers = [](human * a, human * b) {
auto intersec = [](parent_ &a, parent_ & b) {
for (auto elem_a : a) {
for (auto elem_b : b) {
if(elem_a && elem_b && elem_a == elem_b) {
return true;
}
}
}
return false;
};
return a && b && a != b && a->sex == male && b->sex == male && (intersec(a->parent, b->parent));
};
for (auto a : humans) {
for (auto b : humans) {
if(brothers(a, b)) {
std::cout << a->name << " and " << b->name << "\n";
}
}
}
Конечно, текст на С++ получается значительно многословней, чем вариант на Прологе, но по сути, это почти дословное повторение декларативного стиля написания кода. Тем более, не стоит забывать про рассуждения в начале статьи об изначальном выборе концепции при создании языка и «принуждении» программистов пользоваться только одной, изначально выбранной парадигмой разработки.
Таким образом, можно с высокой долей уверенности утверждать, что теоретически можно писать код одновременно в разных стилях.
Но что тогда мешает в рамках одного языка программирования писать с использованием императивного и декларативного стиля? Неужели только убежденность создателей языков в том, что императивный и декларативный стили программирования являются взаимоисключающими? И только из-за этого получаются языки программирования, синтаксис которых подходит для использования одной единственной парадигмы?
Но если это так, то что мешает попробовать разработать синтаксис языка программирования, в котором можно было бы использовать сразу императивный и декларативный стили программирования одновременно?
Что мешает совместить императивный и декларативный стили написания в рамках одной программы?
В любой компьютерной программе всегда существует разделение кода на описание данных и на языковые конструкции для их обработки, т.е. фактически это разделение на данные и на функции. Мне кажется, что основная сложность, которая не дает использовать разные стили программирования в рамках одного языка, это необходимость разделять описываемые сущности на «данные» и «управляющие конструкции». Ведь это свойство (необходимость разделения сущностей на «данные» и «функции»), является неотъемлемой частью любого языка программирования.
Это не удивительно, ведь на заре становления ИТ индустрии, создатели первых языков программирования ориентировались исключительно на императивный стиль, потому что назначением любого компилятора было — преобразовать исходный текст программы в машинные инструкции. И на примерах модульного, структурного и объекто-ориентированного подходах показана и доказана необходимость оформления исполняемого кода в выделенные процедуры с последующей их группировкой в модули и классы.
А декларативный стиль написания программ стал появляться только после создания высокоуровневых языков программирования. И основная цель создания данных языков сместилась на поиск решения конечного пользователя, а не на упрощение генерации бинарных файлов с машинными инструкциями. Если внимательно посмотреть на приведенные выше примеры кода, то можно заметить, что в них определения данных и операторы для их обработки идут вперемешку, (для примера на С++ это определение лямбд функции), что кардинально отличается от императивного подхода.
Так может быть, основная особенность декларативного стиля как раз и заключается в том, что в нем не разделяются «данные» и «действия над данными»? Или как вариант, можно не указывать выполняемые действия над данными вовсе (как в некоторых SQL конструкциях)?
Может быть как раз именно эта особенность (возможность последовательной записи программного кода в соответствии с собственными логическими рассуждениями, в которых могут перемежаться «данные» и «функции», как это происходит при человеческом мыслительном процессе), не дает в полной мере реализовать возможность совмещения императивных и декларативных стилей программирования?
И если это так, тогда можно попробовать разработать такой синтаксис, который будет поддерживать определение как данных, так и декларацию функций в рамках единого потока языковых конструкций!
Проверка гипотезы в новом языке программирования
Для поверки данного предположения, я решил добавить в свой новый язык программирования (синтаксис которого допускает определение функций в одном потоке с описанием обрабатываемых данных), недостающие алгоритмические конструкции, которые позволяли бы реализовать императивный стиль программирования, несмотря на изначальную ориентацию только на декларативную парадигму.
Правда с учетом изначальных ограничений синтаксиса нового языка (запрет на применение операторов в виде зарезервированных ключевых слов), в качестве оператора проверки условия была выбрана синтаксическая конструкция, соответствующая по смыслу термину «следует», т.е. тире и угловая скобка «->».
В результате, условный оператор получился практически математический, который легко объединяется в последовательности для реализации проверки множественных условий вида «else if». А для объединения сразу нескольких логического операторов между собой и для отделения их от последующего действия, операторы проверки условия можно заключать в круглые скобки.
В общем случае условный оператор в новом языке программирования имеет вид:
условие -> действие;
или
(условие) -> {действие};
или
(условие1 || условие2) -> {действие} -> {действие иначе};
Или расширенный вариант, для наглядности записанный с отступами:
(условие1) -> {действие1}
(условие2) -> {действие2}
(условие3) -> {действие3}
-> {действие_иначе};
Тогда и операторы циклов можно будет записать практически аналогично, только выбрав в качестве оператора, не оператор следования, а следование с возвратом (раз уж нужно указывать циклическое действие).
Цикл пока:
(условие) <-> {тело цикла};
счетный цикл для работы с итератором*:
(счетчик_или_данные!)? <-> {тело цикла};
*) Разъяснение операторов «итератор» приводится в этой статьеВ этом случае, решение тестового примера декларативной программы на Прологе можно будет легко оформить, как в императивном, так и в декларативном стилях одновременно!
м := "муж.";
ж := "жен.";
human @= term(пол=, parent=[,]);
Tom @= human(пол=м);
Janna @= human(пол=ж);
Jake @= human(м, [&Tom, &Janna,]);
Tim @= human(пол=м, parent=[&Tom,]);
brother(h1, h2) &&= $h1!=$h2, $h1.пол==м, $h2.пол==м, $h1.parent & $h2.parent;
// Оператор «&» побитовое «И» для чисел или операция пересечения для множеств
// Запись алгоритма поиска решения в императивном стиле
(h1=human!, h2=human!)? <-> {
(brother(h1, h2)) -> {
@print($h1, $h2, «\n»);
}
};
// Краткая запись алгоритма поиска решения в императивном стиле
(h1=human!, h2=human!)? <->
brother($h1, $h2) -> @print($h1, $h2, «\n»);
// Запись поиска решения в декларативном стиле
brother(human!, human!)?;
Примерно это и требовалось показать!
З.Ы.
Я почти закончил эксперименты с синтаксисом языка. И теперь, после добавления в него недостающих алгоритмический конструкций (ветвления и циклов), планирую, что следующая статья о новом языке программирования будет посвящена описанию его полного синтаксиса и публикации исходников прототипа компилятора для возможности поиграться на реальных примерах.
З.З.Ы
В комментариях gbg привел очень простой ответ на один из поднятых вопросов. Причем в более элегантном виде и без написания кода!
Ну и пять копеечек про ООП. Как ни странно:
1) В нем таки есть декларативная часть, все эти pubic, private, virtual, etc
2) Эта декларативная часть, внезапно, элегантно позволяет реализовывать декомпозицию и управление сложностью.
3) При наличии перегрузок, можно поиграть в алгебраическое мышление, рассматривая взаимодействие двух объектов как бинарную операцию.
Ух ты, ООП — это обкатанная практикой технология, которая совмещает декларатив с императивом и вроде позволяет освоившим ее вкусно кушать и мягко спать!
Комментарии (31)
Zanak
26.07.2021 18:24Посмотрите в сторону OCaml.
rsashka Автор
26.07.2021 18:30Посмотрите в сторону OCaml
А где там декларативная парадигма?
Я не спец по OCaml, но про него пишут, что он вроде чисто императивный.
Zanak
26.07.2021 19:55+1Я больше про смешение парадигм. OCaml позиционирует себя как функциональный, императивный и объектно ориентированный язык.
Просто свалить несколько подходов в кучу - относительно легко. Сделать это достаточно удобным образом - куда сложнее, на мой взгляд. И здесь стоит посмотреть в сторону наиболее удачных примеров. Как мне кажется.
arTk_ev
26.07.2021 20:57+1Лучше тогда сразу F#, он от Ocaml и произошел.
F# позволяет писать не только в функциональном стиле, но и в императивном, и напрямую C#-ом.
gbg
26.07.2021 18:49+11А помните, были такие "Компьютеры пятого поколения"? Это такая история про то, как Пролог и несколько миллиардов иен (aka полмиллиарда долларов) ухнули в трубу в 80е годы.
Одна из причин уха была банальной - чтобы надекларировать таким образом серьезную бизнес задачу (не демку, а нормальное такое ТЗ листов на 30-50), и при этом сохранить рассудок всех причастных, нужны какие-то средства управления сложностью и декомпозиции (вроде как ООП с этим кое-как справляется, со скрипом правда), а также люди с декларативно-функциональным мышлением, которых мало. (Потому что такому ремеслу учат на матфаке, а сажать на разработку магазина детских сосок Ph.D, который напишет диссертацию "Алгебраизация сосок. Оператор композиции бутылочки и молочка." - очень накладно. За эти деньги можно нанять десяток слесарей-императивщиков "бери ближе, швыряй дальше").
Автоматизировать создание правил на практике не представляется возможным, потому что теорема Геделя о неполноте формальных систем встает на пути like a boss (Ну или как Гендальф и его YOU SHALL NOT PASS!). Японцы погорели и на этом в том числе - их системы умнели-умнели, а потом внезапно тупели.
То есть, при всем нынешнем Agile и отжайле, где хотелку можно быстренько заткнуть костылем "вооон в том php-шнике", в декларативном конструкте вполне может оказаться, что систему нужно менять буквально всю, если просчитались с декомпозицией на ранних этапах.
Это ни в коем случае не говорит о том, что ваша идея плохая. Но вы ходите по чрезвычайно тонкому льду. И когда (и если!) он провалится, под ним вас будут ждать те самые японцы, а также компьютер с аппаратной реализацией LISP на компонентах с датам 80х годов прошлого века.rsashka Автор
26.07.2021 19:12+1Я понимаю, что лед тонок, но иногда хочется найти что-то большое и светлое, хотя в конечном итоге все действительно может закончится как у "тех самых японцев".
А что касается возможности совмещения разных стилей, то конечный результат зависит не столько от языка, сколько от самих разработчиков и их культуры. Хотя трудно спорить с тем, что к хорошей культуре должен подталкивать и сам инструмент, но в конечном итоге за результат все равно отвечает человек.
Сейчас практически по "Ажаль" я тестирую разные подходы и идеи, и естественно, данная статья является результатом изучения только одного из аспектов, которые в последствии и планирую применить в новом языке.
Спасибо за развернутый комментарий!
gbg
26.07.2021 19:14+2Ну и пять копеечек про ООП. Как ни странно:
1) В нем таки есть декларативная часть, все эти pubic, private, virtual, etc
2) Эта декларативная часть, внезапно, элегантно позволяет реализовывать декомпозицию и управление сложностью.
3) При наличии перегрузок, можно поиграть в алгебраическое мышление, рассматривая взаимодействие двух объектов как бинарную операцию.Ух ты, ООП - это обкатанная практикой технология, которая совмещает декларатив с императивом и вроде позволяет освоившим ее вкусно кушать и мягко спать!
rsashka Автор
26.07.2021 19:18Хм, а ведь точно! Любой интерфейс, это действительно декларативная часть императивного языка!
Реально, большое спасибо! Ответ на один из вопросов оказался даже проще и без необходимости писать код!
motoroller95
26.07.2021 19:34+1Ну кстати про тот же руби можно сказать что он совмещает оба стиля. На руби очень хорошо пишутся всякие dsl, а они в свою очередь и есть тот самый декларативный язык
rsashka Автор
26.07.2021 19:39+2Как написали в комментариях чуть выше, любой ООП в общем виде, это практически декларативный стиль описания классов и интерфейсов + императивная реализация. Поэтому не удивлюсь, если синтаксис руби более приспособлен для декларативного стиля, чем тот же самый С++
Что дополнительно подтверждает вывод о том, что императивный и декларативный стили не являются взаимоисключающими и очень даже совмещаются между собой, лишь бы только синтаксис языка этому сильно не противился.
NightShad0w
26.07.2021 21:52+1Сочетание декларативного и императивного подхода в одном языке - это не только наличие синтаксиса и возможности его использовать.
Императивный подход берет на основу решение задачи. На каждую задачу может быть несколько решений. Реализация конкретного решения с использованием имеющихся инструментов дает императивную реализацию, где выбор решения определяет поведение программы.
Декларативный подход фокусируется на задаче. Для разных задач может подходить одно и то же решение. Если описать задачу, используя абстрактный инструментарий, то можно применять готовые решения. Скрытый решатель не является неотъемлемой частью декларативного подхода. Он скорее расширяет сферу применения конкретной реализации конкретного языка. И в декларативном языке возможность представления разных типов и классов задач определяет вид программы.
Формальное комбинирование подходов, такое как упомянутые ключевые слова для ООП и SQL с императивными командами, несомненно возможно. Но не стоит усилий в силу избыточности. Если язык позволяет выразить задачу исключающим ошибочность описания(что само по себе нетривиально для многих типов задач) способом и предоставляет инструменты вывода решения из готовых или комбинацией готовых, то возможность реализовать отдельное частное решение в этом же языке с использованием тех же или других конструкций уже лишнее. Получится или два языка в одном, или крайне неудобный универсальный инструмент.
OleksiiVoropai
27.07.2021 12:35Формальное комбинирование подходов, такое как упомянутые ключевые слова для ООП и SQL с императивными командами, несомненно возможно. Но не стоит усилий в силу избыточности.
Совмещение SQL с императивными командами давно стало промышленным стандартом - PL/SQL
aamonster
26.07.2021 23:14+1Можно подумать, вы не видели лямбда-исчисление (куда уж декларативнее?) и доказательство его полноты по тьюрингу.
Т.е. технически одно через другое делается легко (мне очень нравится хаскелевский подход, когда функциональная программа на лету вычисляет императивную, которая и взаимодействует с миром), вопрос лишь в том, как грамотно разделить – что удобней делать декларативно, что императивно. Так, чтобы потёкшая императивщина (mutable state, вот это всё...) не сломала декларативную часть.
Mikluho
27.07.2021 10:21+2Как по мне, так важнее не спецификация языка (хотя плохо, если язык ограничивает разработчика), а сам подход.
Каждая парадигма наиболее хорошо описывает определённый круг задач. Например:
ООП хорошо моделирует бизнес-домен
Императивно хорошо описывать пошаговые алгоритмы
Декларативно удобно описывать задачу
Функционально - математику.Использование неподходящей парадигмы приводит к сложности понимания и анализа, а также модификации при изменении исходных условий.
Наоборот, совмещение позволяет получить максимум выгоды от каждого подхода.
Например, в одной из систем, которую я разрабатывал, декларативно описаны валидации, проверки прав, стандартные контроллеры (там контроллер часто содержал только одну строку кода, в которой указывался маппинг входящего запроса на бизнес-сущность, выполняемая операция и маппинг на то, что надо отдать в результате) и т.п.
Императивно были описаны шаги бизнес-логики (например, создание заказа подразумевает около десятка шагов на верхнем уровне, а каждый шаг - ещё одна функция в императивном стиле).
Функционально были написаны расчётные алгоритмы и валидаторы.
И всё это удобно сочеталось с ООП-классами, которые всё это реализовывали.rsashka Автор
27.07.2021 10:26Вы правильно написали, что важен подход. Просто спецификации языка могут не учитывать, или специально усложнять использование одного из стилей написания кода, например, потому что так решили создатели языка.
И тут вопрос не в том, какой из подходов лучше для решения того или иного класса задач, а чтобы синтаксис языка "не ограничивал разработчика" в принудительном порядке.
x-ronos
27.07.2021 11:09После прочтения статьи каша в голове. Императив - это основа, если речь идёт об ИТ, это тот базовый, фундаментальный уровень на который вы неизбежно скатитесь, когда откомпилируете программу в машинные коды и отправите на исполнение. Так что все прочие парадигмы являются дочерними, если вам требуется конкретное исполнение (реализация). Даже в математике, когда вам требуется конкретный ответ (цифра), вы скатываетесь до конкретной последовательности вычислений.
rsashka Автор
27.07.2021 11:37Императив - это основа, если речь идёт об ИТ, это тот базовый, фундаментальный уровень на который вы неизбежно скатитесь
ИТ бывает разный. С императива начинают изучения основ ИТ, и это значительно проще для понимания и объяснения. Но вы используете в своем пример разные уровни реализации, которые сравнивать между сбой бывает не всегда корректно. Ведь если идти в вашем примере еще дальше, то про "конкретное исполнение (реализация)" можно "скатиться" до взаимодействия полей и элементарных частиц. А на таком уровне "исполнения" микропроцессор и человеческий мозг буду очень похожи.
Но ведь дьявол кроется в отдельных деталях, в том числе и в уровне абстракции решаемой задачи. И очень может быть, что при переходе с одного уровня представления на другой, вместо императивного представления исполнения, декларативная форма будет более предпочтительней. Это когда количество переходит в качество, диалектика одним словом ;-)
x-ronos
06.08.2021 12:16+1Диалектика? Ну да, конечно! Переход количества в качество. Отрицание отрицания. Я просто хотел указать именно на то, что многие этого не понимают и рассматривают парадигмы как нечно отдельное. То есть они дошли до отрицания предыдущей стадии, когда отвергают императивную парадигму как основу для возникнования декларативной парадигмы, а до отрицания отрицания они ещё не дошли.
OleksiiVoropai
27.07.2021 13:41+1А Вы знакомы с языками логическо-функционального программирования, такими как Curry или Mercury? Мне кажется, они близки к тому, что Вы хотите предложить. Они позволяют совмещать описания функций и логических конструкций, а также в определенных случаях логически выводить входные аргументы функции, если известен результат ее вычисления.
Такие конструкции как
(условие1) -> {действие1} (условие2) -> {действие2} (условие3) -> {действие3} -> {действие_иначе};
часто встречаются в функциональном программировании. Условие в левой части называется guard и задает условие, при котором может быть вычислена функция слева.
Теоретически функциональное программирование тоже является декларативным, но оно удобно для описания последовательности вычислений. Императивную парадигму совместить с декларативной гораздо сложнее из-за побочных эффектов при вычислениях. Движки декларативных языков часто полагаются на перебор разных вариантов решения, и внешние эффекты в одной ветви поиска могут повлиять на результат вычисления этого же выражения в другой.
rsashka Автор
27.07.2021 14:10С логическо-функциональными языками я знаком, а из некоторых даже позавимствовал некоторые идеи, например процитированная вами синтаксическая контструкция. Правда у меня пока не получается написать лексические правила для её однозначного разбора, но надеюсь, что эту проблему в скором времени решу.
Я не уверен, что функциональное программирование следует относить к декларативным парадигмам. Как уже выяснилось, в том числе и в процессе обсуждения статьи, предположение о том, что декларативный и императивный стили не являются антагонистами и вполне себе уживаются вместе. Поэтому правильнее будет считать, что на функциональном языке можно писать как в императивном, так и в декларативном стиле, а не относить функциональные языки исключительно к декларативным (просто на одном языке в декларативном стиле писать легче, чем на другом).
Что же касается побочных эффектов (проблемы, которую и решает функциональное программирование), то тут боюсь ситуация чем-то похожа на ситуацию с языками, заточенными исключительно на одну парадигму разработки. Создатели функциональных языков загорелись основной идеей и разработали синтаксис, принудительно ограничив использование других возможных подходов.
Я же хочу предложить не конкретную лексическую конструкцию (как говориться, все придумано до нас), а отказаться от навязывания "только правильных" парадигм разработки, и попробовать разработать такой синтаксис, который можно было бы легко использовать как в одном, так и в другом стиле.
OleksiiVoropai
27.07.2021 15:14Проблема совмещения императивного и декларативного программирования, не столько в синтаксисе, сколько в модели вычислений. Например, в основе движка логического вывода лежит поиск с возвратом, при котором перебираются разные решения и один и тот же код может вызываться для разных ветвей поиска. Если результат вычисления этого кода зависит от значения внешней переменной или поля объекта, то может получиться, что в разных ветвях поиска получится разный результат, что может сломать логику поиска.
Например, создатели языка Oz/Mozart пытаются решить эту проблему оборачиванием переменных в специальную структуру под названием Cell и заменой стека вызовов функций древовидной структурой под названием Computational Spaces.
rsashka Автор
27.07.2021 20:07Это проблема конкретного движка, который использует декларативный стиль, но это не является проблемой самого декларативного стиля.
leshabirukov
27.07.2021 15:11+1Можно ли использовать декларативный и императивный стили написания программ одновременно?
Даже до ООП, просто описывая интерфейс функции на С, вы уже декларируете. Другое дело, что этой декларации недостаточно для полного описания функционала и соответственно, возможности автоматической реализации, но и языки позволяющие такое описание делать есть, к примеру `Idris`. К сожалению, пока там всё сложно, по слухам даже правильное описание функции `sort` долго сделать не могли.
Penguinus2008
27.07.2021 20:04Я не совсем понял претензию к SQL
Тем же Select - вы говорите: дай мне это отсюда в таком-то порядке отфильтровав таким-то образом. При этом как это будет происходить вас не волнует. Если бы вы писали в императивном стиле - вы бы уже должны были думать о том, какая там структура данных для хранения, как будете обходить, фильтровать хранить таблицы, объединять и т.д.
Так что, как мне кажется, SQL - вполне себе декларативный. То что нужно часто знать, что под капотом делается, чтобы писать оптимальные запросы - другой вопрос.
rsashka Автор
27.07.2021 20:09У меня нет претензий к SQL. Я писал о том, что сама команда "дай", по сути императивна.
teology
28.07.2021 03:09+1Концепция "условие-действие" реализована в языках МЭК 61131-3 для программируемых логических контроллеров (ПЛК). Более того, микропроцессоры для ПЛК имеют систему команд, изначально заточенную под концепцию "условие-действие".
Зачем эта концепция нужна? Для написания программ с параллельным действием. Процессор пробегается по всем условиям и выполняет действия тех условий, которые выполняются. То есть фактически программный счетчик (Program counter) уже не имеет такого значения как обычно, так как за один цикл порядка 1-10 мс он пробегается по всем (почти) строкам "условие-действие". То есть за 1-10 мс может выполниться от 0 до N действий в зависимости от состояния. Программы с параллельным действием легче писать, каждое условие позволяет легко контролировать эти действия.
Чтобы показать преимущество концепции, нужно взять "параллельную задачу". То есть такую, которая обычно распараллеливается на потоки. Часто такие задачи распараллеливаются с помощью системы "событий-слушателей", которая как раз реализует концепцию "условие-действие". Но на практике условия гораздо более сложны, чем какие-то событийные условия.
zugr
28.07.2021 08:27+1Есть язык в котором можно добавлять всякого рода синтаксические конструкции вполне законно. Forth называется.
Есть наработки порождающие его расширенную версию BacFORTH. Эта версия имеет все конструкции Пролога.
LunaticRL
А вы не смотрели в сторону языка Qml?
Там неплохо совмещены концепции декларативности и императивности
rsashka Автор
Смотрел и даже использую. Но QML это скорее язык разметки с программными вставками, а не настоящий язык программирования.