From f8ff093d8d0e78c974aa9514d718834bb1a985d5 Mon Sep 17 00:00:00 2001 From: klapaucius Date: Wed, 30 Apr 2025 21:50:25 +0500 Subject: [PATCH] april upd. --- hopes.md | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- index.md | 2 + 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/hopes.md b/hopes.md index 2bae3a5..f5c276f 100644 --- a/hopes.md +++ b/hopes.md @@ -12,6 +12,8 @@ - [Коллекционирование ML-ей](#коллекционирование-ml-ей) - [Standard ML 83.4](#standard-ml-834) - [Standard ML 83.6](#standard-ml-836) + - [Карделли против Карделли](#карделли-против-карделли) + - [Standard ML 83.11](#standard-ml-8311) - [Литература](#литература) @@ -297,7 +299,7 @@ Standard ML не предполагается каким-то инновацио При применении функций `f x` в LCF/ML сначала вычислялась `f`, а затем `x`, но в VAX ML сначала вычисляется `x`, что позволило Карделли имплементировать более быстрое применение. В новом стандартном ML порядок вычисления не определен. Что можно посчитать редкой уступкой Карделли со стороны Милнера. Одна из основных конструкций Bare ML - это некаррированная лямбда с несколькими ветвями ПМ как в HOPE (и как, позднее, `function` в OCaml и `\case` в Haskell) -``` +```sml fun v1. exp1 | ... | vn. expn ``` @@ -336,7 +338,7 @@ BareML расширяется функциями ввода-вывода и се Каррированные лямбды и декларации именованных функций определенные через базовые некаррированные лямбды. Определения минимальные, но похоже, что нельзя совмещать каррированные формы с многоветочным ПМ. И определения именованных функций несколькими уравнениями с ПМ не разрешены. Немногочисленные примеры кода написаны в стиле, похожем на стиль в котором написан код на HOPE из [Ryde82]. В случае HOPE это объяснимо тем, что для объявления локальных функций нельзя использовать ничего кроме лямбды с ПМ, но из черновика следует, что у Standard ML 83.4 не должно быть такого ограничения. Некоторые расширения используются, но не синтаксический сахар для каррирования и именованных функций: -``` +```sml infix :: 30 right; rec data 'a list <- nil | :: of 'a #'a list; @@ -370,7 +372,7 @@ map (fun x. x + y, [1; 2; 3]) where y <- 2 Существует более сильный и необычный довод в пользу того, чтоб не считать SML языком, разработанным комитетом. Но к тому, какое желание Милнера, наоборот, не стоит приуменьшать и чем на самом деле является SML мы вернемся позднее. Когда будем писать о временах, в которых все это оказывало более существенное влияние на развитие SML. С помощью "некомитета" Милнер, в июне 83-го подготовил второй черновик описания SML [Miln83b]. Пока писал его, Милнер "убедился, что проблемы ML можно исправить сохранив характер ML". И что из себя представляет этот самый "характер ML"? Опять таки, не совсем ожидаемо, но это HOPE. -``` +```sml type rec 'a list == nil | op :: of 'a # 'a list; val rec map ($, nil) == nil @@ -414,6 +416,113 @@ SML 83.4 выглядел более своеобразным языком, в Для объявления синонимов для типов, `deftype` в LCF/ML (или `type` в Haskell) тоже используются директивы. Почему директивы? В SML 83.4 синонимов не было вовсе. Но все "некомитетчики", которые писали код какого-то заметного размера на ФЯ говорили Милнеру, что они очень нужны. Но даже они по какой-то причине считают их сомнительным хаком, так что они добавлены в язык не как обычная конструкция для объявления типа, а как директива вроде тех, что для раздельной компиляции. Итак, процесс стандартизации ML продолжает отторгать идеи Карделли. Но Карделли был "достаточно добр, чтоб принять это" - продолжает хвалить его Милнер. Карделли написал детальный черновик мануала по VAX ML. И "щедро предложил" приостановить работу над мануалом для VAX ML пока описание SML не будет закончено. Но почему Карделли приостанавливает описание собственного языка? Что он "принимает", в чем его жертва, за которую его продолжает благодарить Милнер? +### Карделли против Карделли + +До сих пор типичный ФЯ Эдинбургской программы был языком, определенным имплементацией. И если такой язык и изобретался на бумаге, как ISWIM до того, он либо так и не был имплементирован, либо то, что было позднее имплементировано довольно заметно отличалось, носило другое название и было типичным определенным имплементацией языком. +Не так с SML. Как Algol 68, SML сначала определен достаточно строго и имплементирован позднее достаточно точно для того, чтоб можно было говорить только о некоторых отличиях между языком и его имплементацией. +Как и авторы Algol 68, Милнер надеялся [Miln83b], что имплементаторы найдут проблемы в описании и самом языке. В отличие от авторов Algol 68 Милнер надеялся, что это произойдет в ближайшие месяцы и серьезных изменений не потребуется. +И у Милнера были основания надеяться. Как мы выяснили в предыдущей части, ко времени появления первых черновиков описания SML уже существовало несколько компиляторов ФЯ, и даже нестандартного еще ML, создатели которых проделали уже значительную часть работы нужной для того, чтоб имплементировать ML стандартный. +МакКвин пишет [MacQ14], что Митчелл, Скотт и Майкрофт, работавшие над форком компилятора VAX ML Карделли в Эдинбурге, начали использовать его для прототипирования дизайна SML после апрельских совещаний. +Тем временем в Кембридже Гордон посоветовал Мэттьюзу написать компилятор SML на Poly. Чем Мэттьюз и занялся [Matt89]. +Как мы помним, сами Гордон с Полсоном не горели особым желанием имплементировать ML, так что вместо того, чтоб переделывать Cambridge ML в компилятор SML Полсон стал первым пользователем компилятора ML Мэттьюза. Но Poly не так похож на SML как ML, так что ничего удивительного, что его автору хоть и удалось имплементировать SML в ближайшие месяцы, но не в самые близкие. +Работающие над форком компилятора Карделли в Эдинбурге были лучше позиционированы для того, чтоб быстро сделать компилятор SML, но в описываемое время были заняты его полным переписыванием. +Поэтому, по всей видимости, первую имплементацию SML и единственную более-менее полную имплементацию SML 83.6 написал Карделли. И даже успел выпустить несколько версий. +Карделли выпустил последнюю версию VAX ML с достаточно подробным описанием [Card83] 14-го августа 83-го года. Она уже содержала некоторые элементы HOPE-фикации, не только названия конструкторов списков как в HOPE, но и экспериментальные простые модули как в HOPE. Это, конечно, не шло ни в какое сравнение с тем, что произошло дальше. +Уже в следующем месяце девятого сентября [Card83b] Карделли начал серию компиляторов SML и переработанных руководств пользователя. Эти версии Карделли называл "позициями". Но, в отличие от танцевальных позиций, позиции у Карделли нумеруются с нуля (Pose O). +Трудно сказать, когда была выпущена первая позиция, в руководстве [Card83c] указана явно неправильная дата - 1-е августа - т.е. раньше, чем нулевая позиция и даже последняя версия VAX ML. Вторая позиция [Card83d] и последняя, (частично) имплементирующая SML 83.6, вышла десятого октября 83-го. +Разница между этими тремя версиями компилятора и их описаниями незначительная. +Разница между ними и последним компилятором VAX ML очень значительная. Карделли не добавил SML как дополнительный фронтенд, как делал в это время автор Poly Метьюз. Карделли заменил один фронтенд на другой. Просто удалил имплементацию своего собственного языка из своего собственного компилятора. Даже разработчики форка его компилятора в Эдинбурге этого не сделали, так что это еще не конец VAX ML. Но, определенно, конец его развития. Карделли пожертвовал своим основным экспериментом - рекордами и вариантами [MacQ14], которые заменены на алгебраические типы данных как в HOPE. Та самая жертва, за которую его благодарит Милнер в своем черновике. +Язык, который имплементируют Pose 0/1/2 отличается от SML 83.6, но предполагается, что отличия временные. Карделли пишет [Card83e], что не собирается больше поддерживать имплементацию собственного диалекта ML. Со временем, или его компилятор измениться чтоб соответствовать стандартному ML или стандартный ML изменится, чтоб соответствовать компилятору. Трудно сказать, почему Карделли все еще надеется на второй исход. +Отклонения имплементации Карделли от SML 83.6 можно разделить на две разновидности [Card83e]. Во-первых, не все эксперименты Карделли с ML закончены. Ввод-вывод частично имплементирует предложенный Карделли новый ввод-вывод для SML. Вместо системы деклараций сигнатур для раздельной компиляции из SML 83.6 в имплементации Карделли простые, непараметризованные модули. Они имплементируют идеи Карделли не полностью и работают не совсем хорошо. +Проверка типов для изменяемых ссылок осталась такой же как в VAX ML, более обобщенной, чем в черновике описания SML. В компиляторе Карделли осталась и поддержка массивов, которых нет в SML 83.6. Надежды на то, что это все может попасть в SML не считаются безнадежными. +Некоторые новые эксперименты из SML, зато, в компиляторах Карделли пока что не начались. Компилятор Карделли еще не поддерживает новые исключения, пока что можно бросать только строки как в LCF/ML. Карделли не имплементировал `local` конструкцию из SML. Не имплементированы полностью проверки для нового, перегруженного сравнения. +Работая над вторым черновиком, Милнер передумал делать порядок вычисления аргументов функции неопределенным и определил его не так как в VAX ML. В новой имплементации SML от Карделли порядок пока тот же, что в VAX ML, т.е. неправильный. +И, после перечисления таких недоработок в имплементации не самых амбициозных инноваций SML, может показаться неожиданным, что самое крупное изменение SML по сравнению с VAX ML - уравнения с паттерн-матчингом - имплементированы. Интересно, что Карделли, когда пишет эти уравнения с ПМ в своем руководстве пользователя компилятора, ставит разделители между уравнениями не в начале строк, как пишущие код на NPL/HOPE и Милнер в своем черновике, а в конце строк: + +```sml +val rec map ($, nil) == nil | + map (f, x::l) == f x :: map(f,l) +``` + +Такой стиль оформления кода не получил особого распространения в среде программистов на SML, но, как мы увидим в дальнейшем, не будет считаться чем-то немыслимым программистами на родственных языках. +Итак, уравнения с ПМ наконец имплементированы. В значительной степени. Карделли не имплементировал сопоставления с числовыми и строковыми константами в паттернах. Хотя `case of` в SML 83.6 определяется через применение лямбды с ПМ, предполагается, что это специальный случай для проверки типов. Но, по видимому, это не(до) имплементировано в компиляторе Карделли. Компилятор Карделли не проверяет линейность паттернов, т.е. то, что новые связываемые имена не повторяются. Поведение в случае если они повторяются не определено. Проверка полноты ПМ тоже не готова. +Но главная часть - компиляция ПМ более-менее современного вида сделана. Теперь и в ML паттерн-матчинг - это не проблема для компиляции, как у имплементаторов языков алгебраической спецификации, а как и в Уорреновском Прологе - решение для получения кода, более быстрого чем Лиспо-образный код с предикатами и селекторами. Решение даже более эффективное из-за линейности ПМ, меньшей мощности паттерн-матчинга, чем в Прологе и в SASL. +В 83-ем году (в худшем случае в 84-ом, когда Карделли описал свой компилятор) уже не просто рассуждают о том, как это можно было бы сделать, как рассуждали в 70-е, а действительно делают [Card84]. Сопоставление образцов оптимизируется для исключения лишних проверок, и представление АлгТД в памяти становится достаточно компактным, чтоб определенный пользователем список был не хуже специального Лиспового. Дополнительная косвенность из-за МакКартиевского разделения на суммы и произведения больше не требуется: + +``` + ┌───┐ ┌───┬───┐ ┌───┬───┐ + │ ├──►│ 1 │ ├──►│ 2 │ 0 │ + └───┘ └───┴───┘ └───┴───┘ + +``` + +специальные объекты без служебных полей в FAM-машине, правда, только для пар и троек, более крупные рекорды хранят еще и свой размер в начале. +Разумеется, это важное достижение стало возможным благодаря существенным наработкам сделанным до того. Карделли не единственный, кто пожертвовал своими собственными экспериментами ради SML. Эта жертва, правда, намного понятнее, чем жертвы Карделли. Потому, что эксперименты просто переехали в SML, экспериментатор получил, по видимому, все что хотел. Этот экспериментатор - коллега Карделли по Bell Labs МакКвин прекратил работу над своим незаконченным компилятором HOPE. И эта работа не пропала даром. +Идеи о компиляции ПМ появились у МакКвина еще в конце семидесятых, во время работы с Жилем Каном [Card84], с которым МакКвин работал в Эдинбурге в 75-76гг над корутинами для POP-2. С Каном мы уже знакомы по истории dataflow-машин. +Идеи получили дальнейшее развитие во время работы над первой имплементацией HOPE на POP-2 в 1979-80гг, вспоминает [MacQ22] МакКвин в 22-ом году. Но и в тот раз идея так и осталась идеей, в той имплементации компиляция ПМ так и не была сделана. Компиляция ПМ этим методом появилась впервые в имплементации HOPE на Franz LISP, которую МакКвин писал в начале 80-х в Bell Labs. И эти наработки были использованы Карделли для оптимизации ПМ в его компиляторе SML [Card84]. +Идеи не были опубликованы, так что другому герою нашей истории, не так тесно связанному с разработкой SML пришлось изобретать компиляцию ПМ независимо и он свои работы опубликовал. +После всех этих лет "дозревания", первое поколение паттерн-матчера МакКвина устарело на следующий же год после первого использования в реальном компиляторе. У МакКвина появились идеи получше. +Но это уже другие истории других компиляторов. + +### Standard ML 83.11 + +В ноябре 83-го был готов третий черновик описания SML [Miln83c]. Последний в этом году и первый дошедший до нас не в рукописной форме. +В предисловии более явно обозначается роль наработок HOPE в исправлении недоработок ML. Оттуда исчезли рассуждения про то, что комитет хороший язык не спроектирует и что SML проектирует совершенно точно не комитет. +В новом черновике Милнер больше не делит авторов SML на действующих и исторических. Теперь они разделены на два сорта. Основные контрибьютеры: Бурсталл, Карделли, Гордон, МакКвин, Милнер, Моррис, Ньюи и Вадсворт. И "также поучаствовавшие" Кузино, Юэ, Митчелл, Монахан, Моссес, Майкрофт, Полсон, Райдхерд, Саннелла, Шмидт, Скотт и Соколовский. На этот раз Суфрин записан в эту вторую категорию авторов. Еще новые люди в ней: Роберт Милн (Robert Milne) и Филип Вадлер. +Революционное предложение использовать ПМ-лямбду из HOPE и новая система исключений, как видим, не помогли Майкрофту попасть в основные авторы. +Главными результатом обсуждений второго черновика, пишет (печатает?) Милнер, было решение о разделении описания SML. +Двумя самыми обсуждаемыми частями языка о которых меньше всего согласия стали ввод-вывод и раздельная компиляция. Но все разногласия о том, каким должно быть ядро языка, касаются мелких деталей. Так что Милнер концентрирует усилия на описании ядра языка без ввода-вывода и модулей. Тем более, что ни выбор того, как именно будет устроен ввод-вывод, ни какие именно модули будут в ML, практически не повлияет на вид ядра языка, утверждает Милнер. +Тут можно было бы предположить, что разделение современного SML на язык модулей и язык функций - это результат действия закона Конуэя и дизайн SML воспроизводит структуру "некомитета", в котором собираются разрабатывать модули отдельно, а ядро языка - отдельно. Но мы уже выяснили в предыдущих частях, что еще в CLEAR, от которого модули МакКвина происходят, уже было явное разделение на язык модулей и язык функций. И оба языка составляющих CLEAR разрабатывали одни и те же люди в одно и то же время. +Описания ввода-вывода от Карделли и модулей от МакКвина должны быть готовы одновременно с третьим черновиком описания ядра SML и все вместе составят полное его описание. +Имплементатор может имплементировать все три описания или только часть из них, пишет Милнер, но "надеется", что все три компонента будут приняты имплементаторами. +Полиморфные изменяемые ссылки все еще считаются недостаточно проверенными временем и Милнер все еще ждет хорошего описания от Дамаша или МакКвина. +Решение полностью исключить типы как у Карделли в VAX ML, а не совмещать их с АлгТД HOPE Милнер называет трудным. +В третьем черновике произведение типов обозначается не `#` (как в HOPE), а `*` (не как в HOPE). Сопротивление Милнера преодолено и теперь паттерн, который сопоставляется со всем - это привычное `_` (как в HOPE), а сравнение и связывание имен привычное `=` (не как в HOPE). SML приобретает еще одну фичу HOPE - `as`-паттерн и теряет одну из самых распространенных фич эдинбургских и обоекембриджских языков - выражение `where`. +Еще одно отступление от / усовершенствование HOPE: теперь можно объявлять каррированные функции несколькими уравнениями + +```sml +val rec map _ nil = nil + | map f (x::xs) = f x :: map f xs +``` + +авторы SML придумали как рассахаривать такой код в лямбды и `case` выражение: + +```sml +val rec map = fun f . fun l . + case (f, l) of + ( _, nil . nil + | f, x::xs . f x :: map f xs ) +``` + +Рекордам Карделли ход в SML закрыт, но какие-то рекорды нужны и в SML 83.11 добавили "рекорды", которые, скорее всего, уже знакомы читателю по другому функциональному языку. Такое вот объявление списка + +```sml +type rec 'a list = nil | op :: of (hd:'a, tl:'a list) +``` + +рассахаривается в такой вот код: + +```sml +type rec 'a list = nil | op :: of 'a * 'a list +exception hd : unit val hd(x::l) = x | hd(_) = escape hd +exception tl : unit val tl(x::l) = l | tl(_) = escape tl +``` + +И наш традиционный пример теперь выглядит вот так: + +```sml +infixr 30 :: +type rec 'a list = nil | op :: of (hd:'a, tl:'a list) + +val rec map _ nil = nil + | map f (x::l) = f x :: map f l + +let val y = 2 in map (fun x. x + y) [1; 2; 3] end +``` + +Третий черновик стандарта вырывается за пределы тесного кружка авторов SML и вращается в несколько более широких кругах подписчиков самиздат-журнала "Полиморфизм". Он опубликован в декабрьском выпуске. Как и обещал Милнер, вместе со статьями описывающими стримы Карделли для ввода-вывода и модули МакКвина. В том же выпуске и руководство пользователя очередной версии компилятора Карделли от 15 ноября 83-го [Card83e]. Теперь предполагается, что период накопления опыта имплементации и использования продлится "год или вроде того". И Карделли не особенно торопится. Конечно, двойные равенства он заменил на одинарные, а `$` на `_`, но менее тривиальные нововведения, вроде декларации каррированных функций несколькими уравнениями Карделли не имплементировал ни в этой "третьей позиции", ни в следующей "четвертой позиции" [Card84b], вышедшей аж пятого апреля 84-го года. +Не известно, насколько полно имплементировал SML экспериментальный компилятор Мэттьюза на основе компилятора Poly, первая версия которого была готова и передана на испытания Полсону в конце 83-го года [Matt89]. + ПРОДОЛЖЕНИЕ СЛЕДУЕТ Литература @@ -423,7 +532,13 @@ SML 83.4 выглядел более своеобразным языком, в [Alle2005]: John Allen. History, Mystery, and Ballast https://international-lisp-conference.org/2005/media/allen-slides.pdf https://international-lisp-conference.org/2005/media/allen-audio.mp3 [Burs80]: R. M. Burstall, D. B. MacQueen, and D. T. Sannella. 1980. HOPE: An experimental applicative language. In Proceedings of the 1980 ACM conference on LISP and functional programming (LFP '80). Association for Computing Machinery, New York, NY, USA, 136–143. DOI:10.1145/800087.802799 [Card82a]: EDINBURGH ML by Luca Cardelli, March, 1982. A README file accompanying the distribution of Cardelli's ML Compiler for VAX-VMS. https://smlfamily.github.io/history/Cardelli-Edinburgh-ML-README-1982_03.pdf +[Card83]: Luca Cardelli, Pre-Standard ML under Unix, August 14 1983. http://lucacardelli.name/Papers/MLUnix.pdf +[Card83b]: Luca Cardelli, ML under Unix Pose 0. 9/9/83 http://lucacardelli.name/Papers/MLUnix%20Pose%200.pdf +[Card83c]: Luca Cardelli, ML under Unix Pose 1, http://lucacardelli.name/Papers/MLUnix%20Pose%201.pdf +[Card83d]: Luca Cardelli, ML under Unix Pose 2, 10/10/83 http://lucacardelli.name/Papers/MLUnix%20Pose%202.pdf +[Card83e]: Luca Cardelli, ML under Unix Pose 3, 11/15/83 http://lucacardelli.name/Papers/MLUnix%20Pose%203.pdf [Card84]: Luca Cardelli. 1984. Compiling a functional language. In Proceedings of the 1984 ACM Symposium on LISP and functional programming (LFP '84). Association for Computing Machinery, New York, NY, USA, 208–217. doi:10.1145/800055.802037 +[Card84b]: Luca Cardelli, ML under Unix Pose 4, 4/5/84 http://lucacardelli.name/Papers/MLUnix%20Pose%204.pdf [Clar81]: Keith L. Clark and Steve Gregory. 1981. A relational language for parallel programming. In Proceedings of the 1981 conference on Functional programming languages and computer architecture (FPCA '81). Association for Computing Machinery, New York, NY, USA, 171–178. doi:10.1145/800223.806776 [Darl76]: Darlington, J., & Burstall, R. M. (1976). A system which automatically improves programs. Acta Informatica, 6(1). doi:10.1007/bf00263742   [Darl81]: John Darlington and Mike Reeve. 1981. ALICE a multi-processor reduction machine for the parallel evaluation CF applicative languages. In Proceedings of the 1981 conference on Functional programming languages and computer architecture (FPCA '81). Association for Computing Machinery, New York, NY, USA, 65–76. doi:10.1145/800223.806764 @@ -445,11 +560,14 @@ July 1981. Dijkstra working note EWD798. https://www.cs.utexas.edu/~EWD/transcri [MacQ15]: MacQueen, David B. The History of Standard ML: Ideas, Principles, Culture https://www.youtube.com/watch?v=NVEgyJCTee4 [MacQ20]: MacQueen, David B., Robert Harper and John H. Reppy. “The history of Standard ML.” Proceedings of the ACM on Programming Languages 4 (2020): 1 - 100.DOI:10.1145/3386336 [MacQ22]: D. MacQueen, A New Match Compiler for Standard ML of New Jersey https://icfp22.sigplan.org/details/mlfamilyworkshop-2022-papers/3/A-New-Match-Compiler-for-Standard-ML-of-New-Jersey +[Matt89]: Matthews, David CJ. Papers on Poly/ML. No. UCAM-CL-TR-161. University of Cambridge, Computer Laboratory, 1989. [Meir83]: Meira, S. R. L. 1983 Sorting algorithms in KRC: implementation, proof and performance. Computing Laboratory rep. no. 14. University of Kent at Canterbury. [Meir83b]: recursion -- again from net.lang srlm@ukc.UUCP (S.R.L.Meira) (08/16/83) https://usenet.trashworldnews.com/?thread=132780 [Miln82]: Milner, Robin. “How ML evolved.” (1982). [Miln83]: Robin Milner. 1983. A Proposal for Standard ML (TENTATIVE). April 1983. 25 pages. https://smlfamily.github.io/history/SML-proposal-4-83.pdf [Miln83b]: Robin Milner. 1983. A Proposal for Standard ML (second draft). June 1983. 50 pages. http://sml-family.org/history/SML-proposal-6-83.pdf +[Miln83c]: A Proposal for Standard ML, by Robin Milner, November 1983. The third draft of Milner's proposal for core Standard ML. +https://smlfamily.github.io/history/SML-proposal-11-83.pdf [Mosses]: Peter Mosses, Affiliations https://pdmosses.github.io/affiliations/ [Muss81]: D. Kapur, D. R. Musser, and A. A. Stepanov. 1981. Operators and algebraic structures. In Proceedings of the 1981 conference on Functional programming languages and computer architecture (FPCA '81). Association for Computing Machinery, New York, NY, USA, 59–64. doi:10.1145/800223.806763 [PERQ1]: PERQ History, 1.3. EARLY DAYS http://www.chilton-computing.org.uk/acd/sus/perq_history/part_1/c3.htm diff --git a/index.md b/index.md index 9a74b5f..d5aaf65 100644 --- a/index.md +++ b/index.md @@ -14,6 +14,8 @@ --------------------------- +[Обновление 2025-04-30](hopes.md#карделли-против-карделли) + [Обновление 2025-03-31](hopes.md#standard-ml-836) [Обновление 2025-02-28](hopes.md#лука-карделли-и-поздняя-эволюция-npl)