Экономический термин delinking впервые (насколько я смог отследить) использовал Самир Амин в работе 1984-го года Delinking: Towards a Polycentric World для обозначения процесса выхода из системы глобального разделения труда. По многочисленными (для нашего немногочисленного Lisp сообщества) просьбам сообщников делюсь своим частным рассуждением о потенциале Lisp-систем в условиях delinking-а с более широкой аудиторией. Это мнение из категории «просто подумалось на досуге», оно не является абсолютно объективной истиной, но, вероятно, может представлять некоторый интерес.

Говорят, что Linux пишут уже 30 лет тысячи программистов по всему миру, поэтому ни одна страна в мире не может повторить Linux локально. Но это утверждение не учитывает, что результатом работы программиста является не конечный продукт, а программа. Конечный продукт, некоторый физический процесс, по программе производит компьютер. В некотором смысле программа подобна чертежу автомобиля. Автомобили производят более 200 лет, меняя подходы, чертежи и конкретные планы производства, но на уровне нынешних знаний и технологий, разработать и произвести автомобиль можно быстрее, чем за 2 столетия. Аналогично, значительная часть труда программистов расходуется на переписывание кода. Программисты кодируют некоторую логику, понимают, что она не подходит или устаревает под давлением новых требований и задач, код переписывают. Знания о том, что и как следует делать, накапливаются.

На уровне наших современных представлений о программной и аппаратной инженерии мы можем написать операционную систему с гораздо меньшими трудозатратами, потому что лучше понимаем и программные, и аппаратные тонкости этого дела. Развился понятийный аппарат, методы, алгоритмы. По нынешним временам, написание ядра OS с минимальной базовой функциональностью - это курсовой проект в MIT. Я сам в юности развлекался написанием микроядер с вытесняющей многозадачностью и планировщиками реального времени.

Однако операционную систему необходимо писать на некотором языке программирования. Здесь дела с трудоёмкостью обстоят не так радужно.

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

Но ядра операционных систем и их оболочки из базовых утилит не нужны сами по себе. Нам нужен многократно больший объём прикладного ПО. Для написания прикладного ПО используют зоопарк языков программирования высокого уровня: Python, Go, C#, Java, Kotlin, Scala, JavaScript, MATLAB, Haskell, TypeScript, Bash, Lua, Julia, Erlang и другие. Мы объективно в них нуждаемся, потому что для C и C++ есть определённые пределы сложности организации ПО, при выходе за которые трудоёмкость разработки начинает резко расти. Это связано со сложностью управления ресурсами. Самая трудоёмкая часть большинства программ (я говорю об инженерии, оставляя разработку алгоритмов за скобками) - это управление ресурсами, обработка ошибок, взаимодействие с окружением и прочие оргвопросы, обеспечивающие данными основные алгоритмы программы. Поэтому без языков, отчасти автоматизирующих эту работу, при преодолении некоторого барьера сложности не обойтись. Языки высокого уровня позволяют программистам сосредотачиваться на основной логике работы программы. Они снижают пороги входа и позволяют, например, математикам больше внимания уделять математическим моделям, а не техническим деталям работы с памятью или виртуальным множественным наследованием.

В развитие и поддержку некоторых из этих языков программирования вложено огромное количество труда: JVM (некоторые не любят эту систему, но нужно признать, это шедевр программистской мысли) нисколько не проще LLVM, хотя и спроектирована на мой вкус более элегантно. И труда не только программистов. Развитие систем типов Scala, Haskell, Rust - относительно крупные научные проекты, в которых задействовано много исследователей.

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

Часто производство ПО связано с существенными непродуктивными трудозатратами. Бывает, что вдохновлённые абстрактными текстами по теории категорий или о шаблонах проектирования менеджеры или программисты вместо решения задачи занимаются выводом на околосолнечную орбиту изящных архитектур. Проводя занятия по курсу "Операционные системы", я часто сталкивался с ситуацией, когда для решения задачи, которая по задумке полностью укладывалась в 50 строчек кода на C, студенты писали по 500 или даже по 1000 строк кода на C++. Эти студенческие решения безупречно следовали учению о шаблонах проектирования, но зачастую не решали задачу полностью, упуская важные аспекты решения. К сожалению, похожая ситуация наблюдается и в индустриальном ПО, когда разработчики самозабвенно реализуют фабрики фабрик фабрик или ищут монады там, где их нет. Я пишу об этом не с целью оспорить важность теории категорий для практики программирования (сам нередко к ней обращаюсь), но чтобы подчеркнуть, что даже такой уровень издержек с лихвой покрывается размерами и эффективностью системы разделения труда планетарного масштаба, в которую вовлечены программисты.

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

В этот непростой момент капитанам it-индустрии имеет смысл обратить внимание на Lisp-системы. Можно написать отдельный текст с обсуждением особенностей Lisp-систем, которые позволяют преодолевать кризис трудоёмкости. Но уместнее последовать призыву Линуса Торвальдса «Talk is cheap. Show me the code» и привести некоторые числа (замеры сделаны в Arch Linux).

  • Пакет компиляторов C/C++ со всеми вспомогательными инструментами документацией и необходимыми библиотеками, построенный поверх LLVM (clang 13.0.1-2, llvm-libs 13.0.1-2, compiler-rt 13.0.1-1) занимает примерно 322MiB.

  • Пакет GCC с необходимыми библиотеками и документацией (gcc 11.2.0-4, gcc-libs 11.2.0-4) - примерно 261MiB.

  • Пактет компилятора MIT Scheme вместе с текстовым редактором, стандартными библиотеками,системой отладки, системой символьной алгебры и библиотекой численной оптимизации для решения задач механики, документацией и исходными кодами этого всего (mit-scheme 11.2-3, scmutils 20220117) - примерно 143MiB.

  • Пакет компилятора Steel Bank Common Lisp вместе со стандартной библиотекой, отладчиком и исходными кодами (sbcl 2.2.2-1) - примерно 62MiB.

  • Компилятор Chez Scheme (chez-scheme 9.5.6-2) - примерно 4MiB.

Если взять только исполняемый машинный код самих компиляторов, то выяснится, что

  • Сlang (файл /usr/lib/libclang-cpp.so.13) занимает около 51MiB,

  • GCC (файл /usr/lib/gcc/x86_64-pc-linux-gnu/11.2.0/cc1plus) - около 28MiB.

  • MIT Scheme (.com-файлы каталога /usr/lib/mit-scheme-x86-64-11.2/compiler) - около 4MiB,

  • компилятор SBCL сложно отделить от образа всей системы, образ (файл /usr/lib/sbcl/sbcl.core) занимает около 37MiB.

Мне представляется, что это даёт некое представление об уровне сравнительных трудозатрат, требующихся на развитие и поддержку перечисленных нетривиальных систем программирования.

В 80-ых годах it-индустрия c Lisp-машинами была на почти текущем уровне развития ПО: с графическими интерактивными интерфейсами, векторной графикой, гипертекстовыми документами в PostScript, мультимедийными графическими редакторами 3D-графики, системами компьютерной алгебры и инженерии (см. видео).

Hidden text

В том числе, что особенно важно для создания самоподдерживающейся системы, существовали CAD системы для микроэлектроники, написанные на Lisp. В экосистеме Lisp были созданы прообразы современных GPU и разработаны методы их программирования.

Сделано это было с меньшими трудозатратами (просто не было такого количества инженеров, как сейчас). На более низком уровне понимания тонкостей программной инженерии. На существенно менее производительном аппаратном обеспечении. Даже наши фабрики микроэлектроники лучше того, что было доступно в те времена.

Кроме этого, порог входа в Lisp-системы для новичков существенно ниже порога входа в C, C++, Rust, Haskell, Java, JavaScript и так далее. Проще разобраться, вероятно, с основами только ассемблера или Forth. А на случай совсем уж полного Апокалипсиса, в библиотеках хранятся бумажные книги о Lisp-системах, по которым даже студенты могут создать с нуля первые версии неплохих интерпретаторов Lisp за пару месяцев, и разработать процессоры (наверное) за пару лет. На этих интерпретаторах можно быстро раскрутить более сложный код.

Таким образом, представляется, что в случае потери связи с глобальной системой разделения труда может быть оправданным более интенсивное применение Lisp-систем (или их аналогов; впрочем, аналоги по уровню документированности, разработанности, простоты, выразительности и продуктивности мне не известны).

В разбушевавшейся кризисной ситуации может возникнуть желание последовать за модными течениями и сделать ставку на продвинутые системы программирования: Rust, Haskell, Scala, Kotlin. Реализация такой стратегии столкнётся с несколькими препятствиями. Во-первых, со сложностью развития и поддержки соответствующих инструментов.

  • Пакет Haskell с необходимым набором инструментов и библиотек (ghc 9.0.2-1, ghc-libs 9.0.2-1, llvm 13.0.1-2, llvm-libs 13.0.1-2) занимает около 779MiB. Без учёта LLVM - около 331MiB. Само ядро компилятора (видимо, файл /usr/lib/ghc-9.0.2/ghc-9.0.2/libHSghc-9.0.2-ghc9.0.2.so) - около 93MiB.

  • Пакет Rust без учёта LLVM (rust 1:1.59.0-1) занимает около 508MiB. Само ядро (видимо, /usr/lib/librustc_driver-7c0e7fab30354592.so) -- около 119MiB.

  • Пакет Scala (scala3 3.1.1-1, jre-openjdk-headless 17.0.3.u3-1) занимает около 203MiB. Без учёта JVM - около 35MiB (следует учитывать, что байткод JVM и формат .jar специально были разработан для как можно более компактного представления программ).

  • Пакет Kotlin без учёта JVM (kotlin 1.6.10-1) - около 73.89MiB.

Во-вторых, работа этих компиляторов и требует существенных аппаратных ресурсов. В то же время, Lisp в режиме относительно эффективной jit-компиляции может работать на процессорах Z80 (см. видео);

Hidden text

PICOBIT Scheme позволяет умещать http-серверы с кооперативной многозадачностью в несколько килобайтов и запускать их на микроконтроллерах; а простейший Lisp влезает даже в загрузочный сектор, позволяя раскручивать из пары сотен байтов полноценную систему.

В-третьих, не смотря на важные для индустрии достоинства более продвинутых языков программирования, они не позволяют быстро работать на широком фронте задач с минимальными затратами труда. Отчасти по тому, что эти системы требуют от программистов высочайшей технической квалификации, что снижает доступность таких инструментов для специалистов в других предметных областях. Видимо, поэтому за 30-лет развития в экосистеме Haskell так и не появились (я отыскать не смог) системы компьютерного проектирования, хотя бы, уровня ICAD, или системы компьютерной алгебры подобные Maxima.

С использованием же различных диалектов Lisp, наоборот, разработали и разрабатывают широкий спектр ПО.

Образ Mezzano Demo 4 содержал пару игр: Doom и Quake - которые сносно работали в виртуальной машине. Любопытно, что это были программы на Common Lisp, код которых был получен транспиляцией оригинальных исполняемых файлов. Это означает, что у современных Lisp-систем всё достаточно хорошо с производительностью.

Микро-тесты показывают, что скорость исполнения кода во многих случаях сопоставима со скоростью исполнения программ на Java. Иногда для этого приходится спускаться на уровень машинных инструкций, но современные Lisp позволяют это делать. Конечно, код, обгоняющий хорошо оптимизированные программы на Fortran, C, Rust или C++, компиляторы Lisp (пока?) генерировать не могут. Однако, как заметил Питер Норвиг, хорошо оптимизированный код на C++ ещё надо суметь написать, и не всегда на это есть время. А быстро написанный код на Lisp зачастую может быть эффективнее аналогичных кодов на C++ или Java.

Подытожить вышесказанное можно так: Lisp позволяет с меньшими трудозатратами разрабатывать сложный код, который может достаточно эффективно исполняться на широком спектре оборудования. Это подтверждается историей и состоянием дел в современном компиляторостроении (компиляторы - сложные программные системы): относительно компактные SBCL и Chez Scheme могут исполнять код с эффективностью уровня более громоздкой JVM. Мой личный опыт промышленного использования Scheme тоже свидетельствует в пользу этого. В эпоху delinking-а это свойство систем семейства Lisp может помочь не уронить продуктивность it-индустрии слишком низко.

Как-то так. Благодарю за внимание.

Комментарии (35)


  1. vkni
    17.03.2022 07:46
    +2

    Всё-таки, есть надежда, что ну не будет такого резкого и ужасного обвала. Хотя, конечно, кому война, кому - мать родна.

    В добавление к перечисленному вами софту можно добавить Stalin - https://github.com/barak/stalin - компилятор Scheme R5, производящий агрессивную оптимизацию всей программы.

    Тем не менее, возникает вопрос - вот есть полигоны для отработки "ИТ апокалипсиса": демосцена и микроконтроллеры, почему там LISP не используется? Ну кроме как В. Луговским.

    Помимо тяжёлого Хаскеля есть лёгкая версия того же - Clean (clean.cs.ru.nl), к сожалению, там нет поддержки многопоточности. И альтернативная ветка статически типизированных фунциональных языков - ML значительно легче, чем Haskell.


    1. mikhanoid Автор
      17.03.2022 08:28

      На Хабре была статья об использовании LISP в микроконтроллерах для программирования умного дома: https://habr.com/ru/post/513128 С Clean и ML я сам, к сожалению, дела не имел, поэтому ничего о них не могу достоверно сказать.


    1. mikhanoid Автор
      17.03.2022 08:40
      +1

      P.S. Наверное, не очень разумно использовать Lisp вообще всюду. Я хотел продемонстрировать, что Lisp-ы не особо требовательны к оборудованию. Однако на Lisp можно достаточно быстро и комфортно написать компилятор специализированного языка (или же просто компилятор C), на котором было бы удобно программировать на низком уровне микроконтроллеры, ускорители или центральные процессоры для достижения максимальной производительности.

      Naughty Dog такая стратегия в разработке Crash Bandicoot и Jak and Daxter помогла выжать из спецпроцессоров Play Station удивительный по тем временам результат. Язык назывался GOAL, это был, если можно так сказать, структурированный ассемблер.


      1. forthuser
        17.03.2022 10:21
        +2

        Lisp можно достаточно быстро и комфортно написать компилятор специализированного языка (или же просто компилятор C), на котором было бы удобно программировать на низком уровне микроконтроллеры,

        Для Форт (Forth) ещё проще сделать реализацию языка и он достаточно активно представлен в нише программирования контpоллеров и не только.


        1. mikhanoid Автор
          17.03.2022 10:23
          +1

          Да, но на Forth систему компьютерной алгебры написать будет сложновато. Интересно, были ли попытки?


          1. forthuser
            17.03.2022 10:37
            +1

            Не знаю, но вроде, на DSSP (ДССП — диалоговая система структурного программирования) были и такие разработки хотя это и не Форт язык.

            А, почему сложно, если с Форт можно сделать любой уровень DSL языка?
            тот же RPL язык программиования использован в HP калькуляторах.

            P.S. А, рассматривался ли в Лисп сообществе конкатенативный язык Factor включающий и парадигму функционального программирования?
            Здесь на форуме dxdy.ru есть некоторый топик по нему объясняющий некоторые моменты Язык Factor
            Интересны, к примеру, механики реализации алгоритма быстрой сортировки сделанного в рамках Factor языка.

            Rosetta Code: Popular Programming Languages
            Factor на 23-ем месте в таком рейтинге решённых задач с ресурса.


            1. mikhanoid Автор
              17.03.2022 13:13

              К сожалению, мой опыт со стековыми языками ограничивается их использованием для промежуточного представления программ в компиляторах. Поэтому не могу ничего сказать. Но могу спросить.

              Как прикладного математика меня интересует возможность работы со сложными математическими объектами. Как в Forth могут выглядеть примеры кода, например, из книги Functional Differential Geometry?

              Как системного программиста, которому приходится работать с размазанными по ЦОДам распределёнными системами, меня интересует, во-первых, реализация в Forth аналога конструкции cond - это выбор варианта по условиям, которые могут быть весьма нетривальными. И, во-вторых, реализация вызовов с текущим продолжением (call-with-current-continuation), которые для меня являются прямо таки спасением в программировании сложных реакций программы на внешние события. Как это всё выглядит в Forth?


              1. forthuser
                17.03.2022 14:50
                +2

                Мне сложно ответить полноценно на заданный Вами вопрос т.к. несколько далёк от темы математических вычислений.
                На Хабр была такая статья Численный FORTH
                к тому же и тот же PostScript тоже стековый язык и по нему есть изданные книги по его использованию в вычислительных задачах.

                Под Форт много чего есть, как The Forth Scientific Library Project
                Forth Foundation Library

                страница профессора J. V. Noble использовал Форт в разных прикладных вычислениях.

                P.S. В Форт возможно моделирование на уровне языка любых хотелок, но, конечно же, это требует некоторого самостоятельного труда, если штатных возможностей языка или той или иной своей или стоонней Форт системы недостаточно.

                ЛЕО БРОУДИ СПОСОБ МЫШЛЕНИЯ — ФОРТ ЯЗЫК И ФИЛОСОФИЯ ДЛЯ РЕШЕНИЯ ЗАДАЧ

                Л. Броуди: НАЧАЛЬНЫЙ КУРС ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ
                ФОРТ
                (книга, конечно, немного устаревшая за неимением выпущенных книг по Форт современных реалий, но основы языка проясняет.) сейчас, конечно, боле-менее в основе разных Форт систем присутствует линия идей стандарта 94года.

                Универсальный DSL. Возможно ли это?

                P.S. И, конечно же книга советских времён по основам Форт
                С.Н.БАРАНОВ Н.Р. НОЗДРУНОВ ЯЗЫК ФОРТ И ЕГО РЕАЛИЗАЦИИ
                (если бы не лихолетье 90-х годов, то возможно Форт не был бы забыт и более широко использовался и в России в разных цепочках и продуктах IT)

                Некоторая ретроспективная подборка книг FORTH (Computer program language)

                Картинка с рускоязычного форума пользователей Форт языка
                image


                1. mikhanoid Автор
                  17.03.2022 15:38

                  Любопытно. Спасибо!


            1. shuhray
              18.03.2022 02:56

              Factor сыроват ещё, взявшись писать серьёзную программу, сразу нашёл серьёзный баг. Пестов его бросил, а тот человек, что остался на хозяйстве, при всём уважении, не заменяет Пестова.


  1. mikhanoid Автор
    17.03.2022 08:27

    Удалено


  1. SemyonSinchenko
    17.03.2022 08:47
    +1

    А что, шуток про скобки не будет?)


    1. mikhanoid Автор
      17.03.2022 08:50
      -1

      А что скобки? Даже Visual Studio поддерживает paredit mode


      1. SemyonSinchenko
        17.03.2022 08:59
        +2

        Ну просто в комментах к любой статье про Lisp есть какой-то из мемов про скобки. Я просто исправил недопущение тут))))

        P.S. SBCL это классно, но почему не рассмотрена тема создания Lisp-интерпретаторов под распространенные VM? А именно тема Clojure? На нем, к слову, написана Metabase и не только. Просто низкоуровневое эффективное программирование и лимит в 3.5 Мб это одна область. Но JVM это просто другая область, там не нужны производительность и размер, там нужна хорошо знакомая всем виртуальная машина.


        1. mikhanoid Автор
          17.03.2022 09:22

          Я не стал писать о Clojure, потому что он опирается не только на JVM, но и на всю экосистему Java, используя довольно много библиотек. Я попытался найти компактные реализации JVM, на которых запустился бы Clojure. Но ничего у меня не получилось. Был бы полезно узнать о таких возможностях, если они существуют.


          1. SemyonSinchenko
            17.03.2022 09:25
            +1

            Так в том и суть, что Clojure позволяет использовать всю мощь экосисиемы Java - десятки тысяч человеко-часов работы программистов, сотни библиотек на все случаи жизни и т.д. Взять тот же JDBC - любая современная хранилка данных имеет свой JDBC-драйвер. А в SBCL все уже не так радужно... И JDBC это лишь малый кусочек.


            1. mikhanoid Автор
              17.03.2022 09:31

              Да, но вопрос: как поддерживать всю эту гигантскую экосистему со всеми её зависимостями? Пока мы работаем всем миром, то всё классно. Но если вдруг случается чучхе?


              1. SemyonSinchenko
                17.03.2022 09:39

                Ну SBCL тоже не в РФ развивают. Все самим с нуля не написать, тут уж без вариантов. Вся эта экосистема - это Open Source и SBCL его часть.


                1. mikhanoid Автор
                  17.03.2022 10:10
                  +1

                  Да. Но open source тоже могут отрезать. Даже если создать полную копию всего, эта копия будет мёртвым грузом. SBCL же относительно компактный проект, который можно поддерживать малыми силами. Я не сторонник того, чтобы всё бросить и писать только на Lisp-ах, но в стратегию выживания it-отрасли имеет смысл включить Lisp-системы.


                  1. vkni
                    17.03.2022 11:57
                    +2

                    С OSS проблема не в отрезании, а в том, что это же заготовка. Если железо сильно деградирует в производительности, то сложные программы придётся очень серьёзно оптимизировать без возможности влить в upstream.


  1. DmitriiPisarenko
    17.03.2022 12:23

    Спасибо за статью

    Что скажете насчет Clojure для прикладного программирования?

    Clojure подобен Лисп на JVM, что позволяет использовать на Лиспе все библиотеки для Джавы.


    1. mikhanoid Автор
      17.03.2022 12:59
      +1

      Clojure - отличный инструмент. Мне нравится. И не только мне. Насколько я знаю, его использует AMD для автоматизации дизайна процессоров. Но нужно помнить о том, что Clojure зависит от большой экосистемы JVM. Если по каким-то причинам она окажется недоступной, поддерживать и развивать Clojure будет трудно.


  1. karambaso
    17.03.2022 13:12
    +1

    К сожалению, снова повторяется ситуация типа "каждый кулик своё болото хвалит".

    Про преимущества языков можно спорить до бесконечности, но такой спор всего лишь свидетельствует о молодости и неопытности спорщиков. На самом деле всё проще - человек привык к своей софтовой экосистеме, и поэтому он её защищает. А переход на другую экосистему затратен по потребному времени, иногда очень затратен, как например в случае с хаскелем. Скорее всего и переход на лисп будет очень долгим.

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

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


    1. mikhanoid Автор
      17.03.2022 15:19
      +1

      Я не говорил здесь о том, какой язык лучше, а какой хуже, потому что для этого сначала надо ввести порядок на свойствах языков. Порядок будет субъективным и частичным, и ничего из этой затеи не выйдет. Поэтому я говорю о трудоёмкости достижения определённой функциональности при программировании на том или ином языке. Относительно объективное представление об этом может дать история it. Эта трудоёмкость важна в случае потери доступа к готовым решениям, чтобы эти готовые решения можно было достаточно быстро запрограммировать.

      Означает ли это, что Lisp - это лучший язык программирования из возможных? Нет, конечно.


      1. karambaso
        17.03.2022 19:18
        +1

        Трудоёмкость = К / У, где У - уровень повторного использования. Уровень же в основном определяется имеющимися в наличии компонентами. Если язык имеет развитое сообщество с открытым кодом, то с компонентами, даже при наличии санкций, проблем не будет. Поэтому предложенную вами трудоёмкость вполне можно сравнить в одинаковых терминах, например по статистике имеющихся компонентов с открытым кодом. Подозреваю, что у лиспа такая статистика сильно хуже, чем у си-подобных языков.

        Правда есть ещё готовые решения от крупных вендоров, которые могут отказать в поддержке, хотя забрать назад свои байты они уже не могут. Но опять же - если сравним количество готовых решений от крупных вендоров для лиспа и какого-то более распространённого языка, то ситуация вряд ли будет в пользу лиспа. И её не исправит отказ некоторых вендоров от поддержки, потому что ничто не будет мешать местным фирмам использовать ранее купленный софт без этой самой поддержки, поскольку она чаще всего практически бесполезна.

        Давайте попробуем честно признать - вам нравится лисп и его инфраструктура, а потому вы и предлагаете его. Всё остальное - попытка оправдать то, что нравится. И я даже за такие попытки, но только если честно признаётся, что ниша на рынке уже занята и реалистичных путей её заполучить, в общем-то, не видно. Соответственно, я за статьи, где указывается на какие-то плюсы языка и инфраструктуры в сравнении со всем остальным. Предлагать же решания для запущенных политических проблем на основе того, что нравится - чистая субъективщина.


        1. mikhanoid Автор
          17.03.2022 19:57

          Прямо во вводном абзаце сказано, что я - любитель Lisp, и состою в соответствующем сообществе. Признаю, что мне нравится Scheme и его инфраструктура. Но язык мне нравится не просто так, а за то, что я могу быстро и эффективно решать сложные сетевые задачи, которые требовали гораздо больше времени при решении их на Go, Bash, C, NodeJS. Динамические контексты и вызовы с продолжением - отличные инструменты для работы со сложными потоками управления. Я бы, наверное, сравнил работу на Scheme по продуктивности с работой на Python, только код в результате получается существенно более компактным и эффективным.

          У того, что мне нравится Scheme, есть причины. Согласен, это субъективный взгляд. Но, ведь, есть и объективные факты. Lisp-машины действительно существовали, и действительно на них был достигнут высокий уровень развития ПО. Компиляторы Lisp действительно проще, чем компиляторы многих других промышленных языков программирования, но достаточно эффективны при этом. На Lisp действительно писали и пишут сложные системы...

          И, мне кажется, Вы ошибаетесь в оценке уровня развитости Lisp-экосистем. Например, Guile Scheme в своей стандартной сборке (без внешних зависимостей) содержит уйму библиотечных функций почти на все случаи жизни. Мне в работе пока не пришлось прибегать ко внешним зависимостям. Код действительно более компактный (за счёт особенностей языка и его системы времени исполнения), в небольшую по размерам сборку умещается уйма функциональности.

          Впрочем, Вы правы, нужно показывать на конкретных примерах. Займусь по возможности.


          1. karambaso
            17.03.2022 23:54
            +1

            Лучше отделять идеологические соображения от практических. Идеологически лисп хорош, я не спорю. Было бы время им долго позаниматься, наверняка он и мне бы понравился (это мысль на основе краткого знакомства).

            Но на практике, то, что вы указали - вызовы с продолжением - есть и в си-подобных языках. Не понял, что имеется в виду под динамическими контекстами, возможно это другое название известного в других языках механизма.

            По библиотекам. Простой пример - пробегитесь по всему циклу создания сайта в интернете. Сколь полна поддержка генерации клиентского html? На сколько генерация поддерживается в IDE? Да даже на сколько удобна IDE для самого лиспа? Просто поддержка со стороны IDE - это очень большая тема, в неё вложено очень много труда, может ли экосистема лиспа похвастаться теми же удобствами? А если ещё начнём разговаривать о доступе к БД? Что там с ORM? Банально драйвера под большинство БД есть? Дальше посмотрите на веб сервисы и разного рода очереди - как у лиспа взаимодействие с подобными системами?

            Это всего лишь базовые вещи, без них придётся разрабатывать с нуля очень многое. Но даже факт наличия какого-нибудь веб-сервера, написанного на лиспе, не означает, что этот сервер поддерживает сравнимое с известными веб-серверами количество функций.

            Ну и до кучи, приведу код, который хочется сократить (это недостаток языка, на мой взгляд), можно ли на лиспе его сделать компактнее?

            int n;

            try { n=Integer.parseInt(intAsString); }

            catch (NumberFormatException e) { return false; }

            int a=n*b;

            ...

            Здесь задача кратко выразить мысль о том, что невалидный текст вызывает выход с негативным результатом, а если текст валидный - продолжаем выполнение метода. Как видите, вместо одной строки получилось 4. А сколько будет на лиспе? Но имеются в виду стандартные библиотеки, а не целенаправленное усилие специально для сокращения количества строк.


            1. mikhanoid Автор
              18.03.2022 08:29
              -1

              Поэтому я ничего не написал об особенностях Lisp, а сказал о достигнутых результатах, о работающих системах. Идеи нужно проверять на практике. Lisp практичен.

              Аналог call-with-current-continuation можно соорудить на Си или Си++. Мне приходилось этим заниматься, но ничего приятного и простого в этом нет: необходимо переключать стеки на низком уровне. Не уверен, что такое можно провернуть в managed языках. Аналоги dynamic-wind мне не знакомы. Больше всего это похоже на окружения процессов UNIX только с перехватом выхода и выхода из них. Что позволяет писать, например, так:

              (define hi
                (with-mutex (make-mutex)
                  (lambda ()
                    (display "acquired!\n"))))

              Всякий раз при вызове hi будет происходить автоматический захват мьютекса. Можно ли на Си такое вручную сделать? Можно. Но на Lisp быстрее и удобнее.

              Код, о котором Вы спрашивали, будет выглядеть примерно так.

              (and-let* ((n (string->number intAsString))
                         (a (* n b)))
                ...
                )


              1. karambaso
                18.03.2022 20:26

                необходимо переключать стеки на низком уровне

                Близкий аналог есть в JavaScript в виде конструкции async await. В Java в процессе доводки (то есть уже доступен, но пока не считается частью спецификации) находится Project Loom, где используются так называемые "виртуальные" потоки, которые по сути есть как раз надстройка над переключателем стеков.

                Передать же куда-то продолжение операции в виде лямбды (которая есть упрощение передачи объекта) можно в большинстве си-подобных языков.

                Ну и контекст - он передаётся с объектом. А если нужен доступ к контексту, вызывающему переданный, то это делается просто передачей параметров соответствующему методу. Внутри же можно и синхронизацию делать, и вообще всё, что позволено в языке.

                За пример пример разбора строки в лиспе спасибо, но там не совсем понятно, что происходит. На сколько я помню, конструкция and-let является чем-то стандартным в лиспе, но как она работает - неочевидно из названия, в отличии от названий в стандартных библиотеках в си-подрбных языках. В этом минус лиспа для новичков.

                Я же думал, что можно получить что-то вроде такого:

                int n=Integer.parseIntOrExit(intAsString,false);

                Здесь предполагается использование некой не реализованной конструкции языка, которая позволяет выполнить return argument; в случае невыполнения какого-то заданного критерия. А значение для argument здесь передаётся как второй параметр. В некотором смысле это потенциально возможный альтернативный вариант обработки исключений.


                1. mikhanoid Автор
                  19.03.2022 01:56
                  -1

                  (and-let* ((var exp)...) body...)

                  вычисляет последовательно значения выражений exp, и присваивает результаты переменным var. Если на каком-то шаге exp возвращает false, то and-let* возвращает false. Если false не встретилось, то выполнение доходит до body, и результатом будет результат вычисления body.

                  То, что Вы ожидали, тоже можно написать, но это не идиоматично и громоздко. Будет выглядеть так:

                  (call-with-current-continuation
                    (lambda (k)
                      (let ((n (string->number intAsString)))
                        (unless n (k false))
                        ...
                        )))

                  (call-with-current-continuation fn), в некотором смысле, создаёт точку возврата (некий аналог setjmp из POSIX), и передаёт её аргументом в fn, в нашем случае это параметр k. При вызове (k value), value становится результатом в этой точки возврата.

                  Можно так объяснить, наверное, да простят меня спецы по Lisp.

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

                  Наверное, в других языках нечто такое когда-нибудь да сделают. Но в Lisp-ах это уже есть лет 30 как. А Lisp-ы ещё и развиваются. Появляются новые интересные выразительные техники программирования. Ждать ещё 30 лет, когда они попадут в другие языки?

                  Promises, кстати, тоже давно появились (под именем futures) в Lisp-ах. Годах, кажется, в 90-ых. И это просто библиотека, не требующая менять ядро языка.


                  1. karambaso
                    19.03.2022 14:04

                    Спасибо за пояснения. Как я понял, and-let, это аналог следующего:

                    if (!expression1 || !expression2 || !expression3 ...) return result;

                    Здесь expression может выполнять присваивание примерно так: (null!=(var=getValue(...)))

                    То есть в лиспе реализация преобразования из строки в целое каким-то образом переходит от одного типа результата (целое) к другому (логическое), что в типизированных языках запрещено (и не зря). Поэтому в типизипрованных языках возникает необходимость в новой сущности - исключении. Если в лиспе есть больше свободы с определением типа результата, то это (на мой взгляд) недостаток языка. Лисп в таком случае уподобляется JavaScript и усложняет предотвращение множества ошибок, связанных с отсутствием строгой типизации.

                    Отсюда следует, что предлагать альтернативное поведение по аналогии с лиспом для сильнее типизированного языка - неправильно. Так нарушается принцип типизации. А без нарушения типизации, видимо, как вы и указали выше, получится более громоздкое решение.


                    1. mikhanoid Автор
                      19.03.2022 20:54

                      Ошибки типизации возникают на практике редко и вылавливаются быстро. Было даже статистическое исследование исходников на GitHub, которое показало, что от типизированности языка количество багов почти не зависит. Оно зависит только от количества строк кода. Чем меньше пишем, тем меньше ошибаемя. Самым "безошибочным" языком в этом исследовании оказался Clojure, в котором динамическая типизация.

                      Ну, и динамическая типизация даёт больше возможностей. Например, сложно написать систему символьной алгебры на статически типизированнои языке: у вас должна в арифметике быть смесь из чисел, векторов, функций, символов, операторов и т.д. Придётся сначала написать интерпретатор бестипового исчисления. В языках с динамической типизацией: Julia, Python, Lisp - такой проблемы нет. У статической типизации есть свои плюсы, но и у динамической типизации они есть.

                      Типы и исключения не должны быть обязательно связаны. Haskell и Rust статически типизированные, но в них обходятся без исключений.

                      and-let* работает не совсем так, потому что определяет новые переменные с локальной для and-let* областью видимости.


                      1. karambaso
                        19.03.2022 21:11

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

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


  1. GBR-613
    17.03.2022 19:18
    +1

    Предлагаю начать с малого и написать интерпретаторы Common Lisp для Menuet и Kolibri. Ими никто не пользуется, а у них, насколько я понимаю, большой потенциал.


    1. mikhanoid Автор
      17.03.2022 20:06

      Отличная идея! Можете по быстрому сориентировать с чего начать? Как написать Hello World?