Итак, L3 язык. Нужно рабочее название. Первое, что пришло мне в голову — Триэль. Но это слишком напоминает известный анекдот — «Шуба с трихуелью есть?».
Поэтому: Тривиль. То есть тривиальный язык третьего уровня. Перестанет быть тривиальным, можно будет сменить название.
Что нужно в тривиальном языке:
- модули или пакеты, скорее модули в стиле Оберона, чем пакеты в стиле Го, потому что проще.
- импорт/экспорт
- переменные, константы, функции
- примитивные типы — знаковые целые, вещественный, логический, символ и строка
- конструкторы типов — динамические массивы и классы
- операторы: присваивание, условный, выбор, цикл
- выражения: обычный набор арифметики и логики, композиты массивов и классов, проверка типа, конвертация
Вроде все что нужно. Как ни странно, думать надо скорее над лексикой, чем над синтаксисом. Просто потому, что все хочу на русском. И хочу идентификаторы с пробелами.
Следующий шаг — грамматика.
Постоянная ссылка
Язык подразумевает подсчёт ссылок, но эта особенность не раскрыта. Например, если представить, что правильная программа на этом языке теперь скомпилирована как программа на языке со сборкой мусора, то она всё ещё верна, или окажется некорректной из-за того, что программа зависит от детерминированного освобождения?
Постоянная ссылка
Интересный вопрос. А должна ли правильная программа, скомпилированная в одних предположениях об окружение, работать в окружении с другими условиями?
Постоянная ссылка
Очевидно, что если другие условия несовместимы, то в общем случае не должна, а если совместимы, то должна. В проектируемой системе это вопрос выбора, но вы его не осветили. Вы сообщили про выбор некоторых возможностей языка, но не раскрыли выбор варианта подсчёта ссылок.
Давайте в качестве примера, рассмотрим некоторые варианты.
0. Автоматический подсчёт ссылок, но присваивания только явные. Немного похоже на ручное освобождение, но невозможно создание висячих ссылок, и для уменьшения счётчика достаточно любого замещающего присваивания, а не только 0.
1. Подсчёт ссылок вместе с автоматическим проходом по освобождаемым структурам для уменьшения счётчиков содержащихся в них ссылок.
2. Подсчёт и за-0-ие по цепочке вместе с возможностью привязки пользовательского кода к процессу создания и освобождения.
Код для 0 верен и для 1, для 1 верен и для 2, но не обратно. Код для <=1 также верен и для сборки мусора, а для 2 — нет.
Постоянная ссылка
Я рассматривал только вариант 0. А теперь задумался.
Есть ли где-то толковое сравнение? С аргументацией.
Постоянная ссылка
Вряд ли, с учётом того, что все детали зависят и от других особенностей языка.
Постоянная ссылка
Пакеты в стиле Оберон надо сделать с фишкой из Го — надо оставить как минимум на уровне тестов. Очень удобно разделить код и тесты, но чтобы при этом из тестов был доступ к приватным полям кода. Устойчивость на внезапные изменения проверять очень удобно.
Импорт только квалифицированный.
Символ — определение не верное. Литеры. Символом является вообще всё, что наделено семантикой.
Выделять специально конструктор, имхо, смысла нет. Придётся предусматривать возможность нескольких конструкторов.
Обязательно строгое принудительное приведение типов.
Вместо массивов предлагаю оставить одни срезы и словари.
Постоянная ссылка
Вы хотите в «тривиальном» языке сделать классы???
Мы много говорили о компонентном подходе, и, на мой взгляд, если делать что-то экспериментальное в этом направлении, то надо делать поддержку иерархической вложенности для модулей (как в Rust) и для классов (aka компонент) как… как нигде нету? или как в Вир’е? С ограниченной видимостью, естественно, чтобы «то, что внутри — остается внутри».
Потому что имхо, это фундаментальное/парадигмальное изменение относительно текущего «плоского» ООП, и позже это будет добавлять мучительно сложно.
P.S. В своих упражнениях выходного дня я набрасываю это в виде набора макросов на Rust. Rust оказался прикольным «фреймворком» с базовой поддержкой иерархии модулей, правда, с недостаточно гибкими ограничениями видимости.
Постоянная ссылка
Тривиальному языку — тривиальные классы. Почему нет? ООП — это одна из парадигм, которая должна быть поддержана, хотя бы потому в компиляторе удобно использовать.
В любом языке семейства кроме компонентных возможностей (сборочного программирования), должны быть возможности для синтезирующего программирования и они должны быть достаточно полны. Полнота языка, в первую очередь, определяется полнотой типовой системы. Примерно такой, как здесь: http://алексейнедоря.рф/?p=394
Постоянная ссылка
Покачаю этот контртезис (про «должны быть достаточно полны»).
По той же логике в любом языке структурного программирования должна быть возможность и для GOTO из любого места программы в другое место.
Откуда мы знаем, что для «достаточной полноты» в языке нужны «свободные классы» (назовем так классы в стиле C++/Java/C#)?
Не показывает ли позитивный выход эксперимента с Вир обратного?
Структурное программирование началось с теоремы о том, что достаточно последовательности, ветвления, цикла и подпрограммы. Увы, у меня нет хорошей теории с доказательством, но есть нахальная гипотеза — _достаточно_ иметь «герметичные компоненты» с интерфейсами для композиционной работы, классы данных (immutable values и mutable агрегаты без внешних зависимостей), плюс структурное программирование для работы по низам, а «свободные классы» приносят больше вреда, как GOTO, попав не в те руки.
Постоянная ссылка
Забавно, но структурное программирование не начиналось с теоремы про достаточность последовательности, ветвления и цикла, потому что, упрощая, доказательство сводится к возможности имитации неструктурных переходов с помощью «структурных», что само по себе не приносит итоговой программе никаких полезных свойств. Применимость структурного программирования доказывается сугубо эмпирически, и как мы видим, этот взгляд не победил практически нигде.
Можно уверенно предсказать, что без должного понимания, ограничения «герметичных компонентов» начнут обходить так же быстро и беспощадно, как любые другие формальные ограничения.
Постоянная ссылка
> Применимость структурного программирования доказывается сугубо эмпирически, и как мы видим, этот взгляд не победил практически нигде.
Ну а на мой взгляд — победил везде, потому что для меня достижение структурного программирования не в крайностях («только одна тока выхода из процедуры»), а в смене мейнстримного способа работы. С тех пор никто и нигде не строит программы на спагетти из GOTO. Но для этого потребовалось пожить с языками без GOTO и с удобными готовыми абстракциями взамен.
Хороший вопрос — как именно подтянулось «понимание». Беда еще в том, что масса программистов сегодня гораздо больше, чем тогда, и объемы внедрения «современного» ООП тоже огромны.
Постоянная ссылка
Если объявить ключевые особенностями крайностями, а некоторую имитацию объявить «структурным программированием 2.0», то да — победило.
1.return из середины, если Вы про него не добавляет новой точки выхода из процедуры, зато добавляет его ветвлению и циклу, как и break с continue. Исключения дают альтернативные выходы уже и для процедур. Соответственно, в современных языках есть некоторый зазор по каким именно конструкциям возможен альтернативный выход, но, в целом, к ним относятся достаточно свободно.
2. Когда современному программисту нужно сделать спагетти, он использует не GOTO, а одну из его имитаций, например, while switch. Некоторые построили на этом целую школу(смотрите «автоматное программирование», которое о другом, но сделавшее возможность неструктурности своей неотъемлемой частью незаметно от самого автора)
Всё дело в ключевой особенности программирования — алгоритмической полноте, которая позволяет работать с одними и теми же понятиями через очень разные базовые определения.
Тоже самое будет и с чем угодно, включая, например, ограничениями на импорт. Объявят невозможность произвольного импорта по запросу самого модуля крайностью, и сделают очередное «нельзя, но можно», которое, даже не исключено, может быть и лучше, чем просто «всё можно».
Постоянная ссылка
Не уверен, что я понимаю термин «свободные классы», но могу предположить, что это классы, определяющие объекты, которыми можно обмениваться между компонентами.
Использование произвольных классов для взаимодействия компонент надо или запрещать или строго ограничивать. Примеры такие есть, например, в Swift есть понятие Sendable types:
Что же касается программирования внутри компоненты, или внутри узла компонент, которые не «изолированы» друг от друга, то есть работают в одной области памяти, то почему бы не использовать классы? Изолированные — это из Dart, см. isolates.
Постоянная ссылка
Наверное термин я взял неудачный. Под «свободными классами» я имел в виду классы по типу Java/C#, которые «слишком свободны в том, чтобы делать что хотят». «Фривольные» классы Из-за этой фривольности, попавшей не в те руки, понять, как устроена и как работает программа, бывает невозможно без того, чтобы полностью прочитать весь код.
Например, потому что любая часть кода может обратиться к какой-то «статической» зависимости с побочным эффектом типа DateTime.now(). Или записать что-то в какой-то файл. А по внешнему образу класса этого никак не скажешь.
Ну то есть, это как та подпрограмма (скажем, на ассемблере), изнутри которой вдруг есть безусловный переход куда-то в совсем другое место, и ты об этом не узнаешь, пока не прочтешь весь код.
Спасение, на мой взгляд — в том, чтобы начать делать жесткую специализацию разновидностей классов и их возможностей. Также как if/while/procedure — это специализации по типичным и достаточным способам применения условного и безусловного перехода.
В языках это начало появляться, но пока мало-мало.
Mutable/Immutable Data classes — первый шаг в эту сторону. Класс данных, в которых если и есть код, то он не абы для чего, и не лазит в файлы и в сеть, а, например, для валидации данных, преобразования в другие значения, и все. Это хороший первый шаг. Причем Immutable Value внутри может содержать только другие Immutable Value , но ника не Mutable. Проблески этого принципа, эдакой «жесткой раскраски» типов данных/классов/структур видны много где.
ИМХО, надо этот тренд усилить и сделать второй шаг: побочные эффекты должны доставляться исключительно через интерфейсы и «класс-компонент» должен мочь обращаться только к тем интерфейсам, которые ему явно выданы. И включать внутрь себя дочерние компоненты, которые тоже не могут вытащить интерфейс типа SystemClock или FileIO «из воздуха/static/контекста потока», а могут только получить их от родителя.
Постоянная ссылка
«А по внешнему образу класса этого никак не скажешь.»
Не скажешь из-за игнорирования секции импорта или из-за применения косвенной адресации? Можете рассказать об этом не только здесь, но и в более подходящем месте.
Постоянная ссылка
> Не скажешь из-за игнорирования секции импорта или из-за применения косвенной адресации?
1. Во многих языках импорт это всего лишь сокращение System.IO.File до File.
2. То, что у вас модуль импортирует другой модуль, еще не означает, что один из, скажем, трех классов этого модуля, непременно использует этот импорт.
3. Да и вообще — импорт может быть, но не использоваться. Или может быть импорт большого пространства имен, а использоваться только какая-то безопасная вещь из него, типа System.DateTime.Add(dateValue, durationValue).
4. Да, кажется, я именно об этом, хотя не уверен, что «адресация» тут лучший термин. Модуль/компонент может и не импортирует ничего «такого» сам. Вот только он создает и использует компонент из второго модуля, а тот — из третьего, а третий — бац! и оказывается тащит что-то из файловой системы или из сети. И по первому это не скажешь, пока глазами не прочитаешь всю цепочку. И участь того, кто разбирается с этим — генерить какие-то ужасные графы по импортам и анализу вызовов и пытаться понять, что происходит.
Гипотеза в том, что это излишняя и неуправляемая гибкость.
Одно из интересных свойств ФП в том, что факт/разновидность побочных эффектов там виден сразу, по типу функции. Если у нее нет в типе монады IO, извините за выражение, то значит точно нет ввода-вывода, кого бы она не вызывала внутри себя косвенно. Это любопытно. И с этим как-то живут. Я думаю, можно это свойство вытянуть в ООП-языки, не таща остальных странных ограничений ФП.
ИМХО это повысит анализируемость (а для человка — понимаемость) программ. Кстати, именно из-за недостатка анализируемости Дейкстра начал разбираться с GOTO.
Постоянная ссылка
Пункты 1-4 — это лишь следствия того, что разработчикам инструментария было недосуг, потому что всё это узнаваемо и запрещаемо без изменения корневого языка, но я спрашивал, всё-таки, про косвенную адресацию — то, что позволяет обмениваться функционалом горизонтально, и поэтому это единственное, что нельзя ограничить без изменения языка.
Но за этим исключением мне непонятно, почему Вы как архитектор говорите о достаточности ограничений как гипотезе, а не задаёте архитектуру, в которой ограничения бы действительно были включены. Никто не даёт своего согласия на некоторую дополнительную работу? С переиспользованием библиотечного кода, написанного по-другому, разумеется, возникнут накладные расходы.
Постоянная ссылка
> Тоже самое будет и с чем угодно, включая, например, ограничениями на импорт. Объявят невозможность произвольного импорта по запросу самого модуля крайностью, и сделают очередное «нельзя, но можно», которое, даже не исключено, может быть и лучше, чем просто «всё можно».
Именно так. Сперва находим причину гадости. Объявляем ее злом. Пытаемся жить совсем без нее. Получается тоже так себе, хотя возможно. Ослабляем радикальность суждений, но !!! уже понимаем как применять «гадость» редко, специализированно, и с умом. Поддерживаем на уровне языка правильные способы как основные. И это меняет мышление следующего поколения программистов, которые начинают с этого языка.
Пытался рассказывать об этом вот тут:
https://bespalchuk.ru/presentations/archdays-language-on-the-way-to-devarch/
Постоянная ссылка
Тут есть неоправданные, как мне видится, предположения.
Например, из большинства распространённых языков никогда не исключали неструктурные переходы в целом, и разработчики на них никогда не учились жить без них. И если они и программируют сносно, то не из-за попыток жить без «гадости». По опыту также скажу, что программировать на ЯВУ можно исключительно в структурном стиле, и будет получаться хорошо, но большинству разработчиков на дополнительную ценность структурности наплевать, а вот на дополнительную цену в разработке, которую она тоже имеет, совсем не наплевать.
Постоянная ссылка
> Тут есть неоправданные, как мне видится, предположения.
Очень может быть. Это лишь ряд моих гипотез.
> Например, из большинства распространённых языков никогда не исключали неструктурные переходы в целом, и разработчики на них никогда не учились жить без них.
Не исключали — да. А вот не учились без них — не согласен. В мое время в школе и в вузе уже объясняли, что goto — «зло» И таки учли обходиться без них. Но при этом в том же вузе мне преподавали такое ООП, которое сегодня я не могу назвать иначе как obsessive/stupid/crazy OOP. «Унаследуем объект Планета от объекта Орбита».
> И если они и программируют сносно, то не из-за попыток жить без «гадости».
Конечно, как научили(сь) — так и программируют. Одна из моих гипотез — язык и базовая парадигма сильно определяют способ мышления программиста. Если мы хотим чтобы они дизайнили mid-level софта лучше — нужен такой язык и такая парадигма, которые будут подталкивать (ну и вузы помогут) к более эффективному способу мышления и проектирования.
> По опыту также скажу, что программировать на ЯВУ можно исключительно в структурном стиле, и будет получаться хорошо
Можно. Если ввести паттерн и следовать ему. На мой взгляд современное качественное ООП уже накопило критическую массу таких паттернов, так что пора их опрокидывать в язык, асфальтировать натоптанные тропинки. Я в докладе пытался показать в т.ч. и жизненный цикл приемов программного дизайна — от паттернов до элементов языка.
> , но большинству разработчиков на дополнительную ценность структурности наплевать, а вот на дополнительную цену в разработке, которую она тоже имеет, совсем не наплевать.
Конечно, наплевать. И все же они не лепят столько GOTO сколько было 60 лет назад. И мы не говорим про черно-белое качество и черно-белое стремление к нему и черно-белую экономику. Я считаю, что если за счет новой базовой парадигмы внедренной в язык мы даже просто _слегка_сместим_ «естественный» (выученный с детства) стиль мышления программистов в сторону более системного мышления с герметичными компонентами и активным использованием иерархии компонент, то в среднем качество дизайна улучшится и в среднем экономика разработки сложного прикладного ПО тоже улучшится.
Постоянная ссылка
Понятное дело, что «никто и нигде» было бы преувеличением, но, в целом, не учили, и Вы отдельным примером не опровергаете это даже для конкретного случая, а сверх того, скорее, подтверждаете. Не могу знать, что именно происходило, но в Вашем изложении всё выглядит как провозглашение преподавателями чего-то, что было не понято даже ими, не говоря уже про педагогику. Спор, в чём-то, терминологический, но я не это имею ввиду под словом «учились».
Недаром под конец жизни Дейкстра отмечал, что краеугольным камнем его славы стала наиболее часто цитируемая его статья, но чаще всего цитируемая авторами, которые кроме заголовка ничего о статье и не знают. В этом нет ничего особо трагичного, просто факт.
Постоянная ссылка
> … но я спрашивал, всё-таки, про косвенную адресацию — то, что позволяет обмениваться функционалом горизонтально, и поэтому это единственное, что нельзя ограничить без изменения языка.
Наверное я не до конца понимаю, что вы называете косвенной адресацией. Можете еще пояснить? Что значит «обмениваться функционалом горизонтально»?
> Но за этим исключением мне непонятно, почему Вы как архитектор говорите о достаточности ограничений как гипотезе, а не задаёте архитектуру, в которой ограничения бы действительно были включены.
Это можно делать, конечно. Но при этом эффективность команды сильно снижается при текущих технологиях. Греем воздух. Потому что вы как архитектор в специальных очках видите «линии на местности», по которым надо ходить, а 5-10 котов, которых вы пасете, их не видят, и ходят как попало — ведь язык для них не показывает никаких запретов/барьеров. И выразить то, что вы видите, на том же языке и положить в тот же проект вы тоже не можете — в языке нет для этого средств. И вот вы бегаете с бумажными диаграммами high-level, а разработчики скрипят мозгами привыкшими к Java/C#, и тратят мыслетопливо на то, чтобы в уме представлять маппинг уровня классов ЯП на ваши немашиночитаемые диаграммы и не отклоняться. Делать эту работу человеку в 21 веке — позор Это должна делать машина. И разработчики должна сразу осваивать эти волшебные очки — за счет того, что язык и машина тоже видят эти линии.
> С переиспользованием библиотечного кода, написанного по-другому, разумеется, возникнут накладные расходы.
Это да… Конечно… Но что делать… Можно экономить на плавных переходах по типу того, как делали в .NET с введением null safety — разметкой существующих библиотек вместо переписывания. Разметка, обертка, выделение «pure» участков, которые легко потребовать, чтобы они были pure, и т.п.
Постоянная ссылка
>Наверное я не до конца понимаю, что вы называете косвенной адресацией
Наверно, стоило назвать это полиморфизацией :). Но для верности лучше кодом — http://vostok.oberon.org/sandbox.html?view=28hek8loyh55-horizontal_spreading
Горизонтального перемещения можно добиться и по-другому — https://vostok.oberon.org/sandbox.html?view=9r4fqn3f6a43-only_mem_horizontal_spreading
>И выразить то, что вы видите, на том же языке и положить в тот же проект вы тоже не можете — в языке нет для этого средств
Я не агитирую, сам предпочёл бы пользоваться более прямым языком. Но Вы уверены, что в имеющихся средствах + смекалке и владении программированием невозможно это отобразить?
>Делать эту работу человеку в 21 веке — позор Это должна делать машина.
Архитектура в виде документации на протяжении такого количества времени — да, это позор, потому что она должна быть в виде кода, хотя и необязательно в виде текста, но обязательно проверяемого машиной наравне с остальным кодом. Но мне видится, что архитекторы в массе своей, даже если сами того не подозревают, заинтересованы в 1-м. Документация всё стерпит, а с кодом в проекте так не получится.
Постоянная ссылка