Я программирую с восьми лет, и основную часть жизни был разработчиком в том или ином качестве. На протяжении своей жизни кодера, как любителя, так и профессионала, я изучил множество языков программирования, которые казались клонами друг друга. Но было и несколько языков, изменивших мой взгляд на программирование, а то и мышление в целом.
Я перечислю их в том порядке, в котором узнавал о них. В большинстве случаев я буду ассоциировать функциональность с языками, в которых они появились не впервые. Это не ошибка, я просто хочу показать, когда узнал об этих возможностях.
▍ Basic
Язык, разработанный для того, чтобы сделать программирование простым.
- Взрыв мозга: программирование собственных игр!
- Взрыв мозга: массивы (или
DIM
, как они в нём назывались)! - Взрыв мозга:
GOTO
! - Взрыв мозга:
GOSUB
!
Первый Basic с нумерацией строк давно мёртв, но его наследие живёт в VB.Net, VBA, а также в современных динамических языках (Python, JavaScript и так далее).
▍ (Turbo) Pascal
Наверно, этот язык сильнее всего повлиял на моё формирование как программиста. Подростком я выпустил несколько приложений на Turbo Pascal, в том числе и одно коммерческое (хотя, если откровенно, основную часть работы выполнил Алан Фриш).
- Взрыв мозга: туториал по программированию!
- Взрыв мозга: IDE!
- Взрыв мозга: компилятор действительно помогает находить твои ошибки!
- Взрыв мозга: по-настоящему полезный отладчик!
- Взрыв мозга: структурированное программирование!
- Взрыв мозга: юниты («модули»)!
- Взрыв мозга: объектно-ориентированное программирование (хоть и не особо полезное в библиотеках)!
- Взрыв мозга: тулкит для создания UI!
- Взрыв мозга: динамическое распределение памяти!
- Взрыв мозга: сообщество разработчиков (а также SWAGS)!
- Взрыв мозга: оверлеи памяти!
Несколько лет спустя, всё ещё подростком, я попробовал перейти на C, и был немного разочарован, обнаружив, что, по сути, это такой же язык, только читать его сложнее (что за странный цикл
for
с тремя компонентами?), он больше подвержен ошибкам (дальние указатели? серьёзно?) и с зияющей нехваткой некоторых возможностей (как это так — нет модулей?), поэтому я не буду включать C в этот список.Язык Pascal по-прежнему существует как Turbo Pascal, Lazarus Pascal и Delphi, но ещё и продолжил своё развитие в Ada (из-за его синтаксиса) и во всей существующей документации (JavaDoc, rustdoc, Sphinx, Doxygen и так далее), ведь первый инструмент для разработки документации (WEB) был спроектирован для и на Turbo Pascal.
▍ x86 ASM
Одно из основных ограничений Turbo Pascal заключалось в том, что его BGI (API для общения с графическим адаптером) был ограничен 16 цветами и относительно низкими разрешениями. Когда появились более современные графические карты, все разработчики внезапно начали учить язык ассемблера, чтобы иметь возможность общаться с устройствами. Я не стал исключением.
- Взрыв мозга: адресация памяти!
- Взрыв мозга: стек — это иллюзия!
- Взрыв мозга: прямое взаимодействие с графическим адаптером!
- Взрыв мозга: прерывания!
- Взрыв мозга: регистры!
Сегодня очень немногие люди пишут на языке ассемблера вручную, потому что это не особо полезно и не портируемо на 100%, но, разумеется, он всё равно присутствует внутри вашего компилятора или JIT. Я очень часто вижу, как разработчик на Rust или C++ демонстрирует дизассемблированную версию своего двоичного файла, и это напоминает мне о старых (не таких уж) добрых временах.
▍ HyperCard
На другой части спектра находился HyperCard — язык программирования, придуманный для тех, кто не хочет изучать языки программирования. Он выглядел как приложение для рисования, но можно было использовать встроенный язык, чтобы превратить его в полнофункциональную IDE, а также писать новые программы.
- Взрыв мозга: настройка своей среды.
- Взрыв мозга: мой первый скриптовый язык, хотя в то время я этого и не понимал.
- Взрыв мозга: программирование на почти естественном языке.
- Взрыв мозга: стек и шаблоны, мои первые абстракции UX.
- Взрыв мозга: программирование как форма беседы между программистом и компьютером.
- Взрыв мозга: сборка мусора (хотя тогда я этого ещё не осознавал).
- Взрыв мозга (в плохую сторону): невозможность портирования кода на платформы, не связанные с Apple; вероятно, это одна из причин того, что сегодня я в основном придерживаюсь опенсорса.
Наверно, этот язык стал первоосновой для всего «визуального», от Visual Basic
и Visual Studio до любого инструмента для рисования UX. Кроме того, в какой-то степени, HyperCard стал одним из предков веба. Время от времени я наблюдаю возрождение какого-нибудь потомка HyperCard, но надолго их обычно не хватает.
Разумеется, многие из этих идей используются и в SmallTalk; вероятно, он более революционен, но я изучил SmallTalk гораздо позже, поэтому взорвать мой мозг по этим пунктам ему уже не удалось!
▍ (O)Caml
Caml (позже OCaml) стал для меня неожиданностью. Я был пресыщенным студентом-первокурсником, уверенным, что после выпуска коммерческого ПО знаю уже всё; обычно я лучше владел программированием, чем учителя программирования в старшей школе, и это, разумеется, означало, что я лучший во всём. Я прочитал документацию OCaml ещё до первой лекции и ошибочно понял «полиморфизм» (сегодня мы называем это «дженериками») как «слабую типизацию». Как же я ошибался. На первой лекции преподаватель показал нам, как обходить дерево на OCaml. Это был крайне лаконичный, надёжный и читаемый код. Кроме того, преподаватель оказался сильным разработчиком и автором одного из текстовых процессоров, так что это, разумеется, стало столь необходимым мне уроком смирения.
Я влюбился в OCaml. Он быстро стал моим основным языком программирования до выгорания, вызванного разработкой Opalang.
- Взрыв мозга: параметрический полиморфизм (aka «дженерики»)!
- Взрыв мозга: выведение типов!
- Взрыв мозга: сборка мусора!
- Взрыв мозга: сопоставление с образцом!
- Взрыв мозга: REPL!
- Взрыв мозга: программирование высшего порядка!
- Взрыв мозга: монады!
- Взрыв мозга: Continuation-Passing Style!
- Взрыв мозга: конкурентность передачи сообщений с произвольными сообщениями (включая каналы)!
- Взрыв мозга: полиморфические варианты и рядный полиморфизм (aka «статическая утиная типизация»)!
- Взрыв мозга: изменяемый синтаксис!
- Взрыв мозга: написание DSL!
- Взрыв мозга: Multistage programming!
- Взрыв мозга: программирование на уровне типов между модулями!
Сообщество OCaml по-прежнему очень активно. Он использовался в разработке языков программирования (в том числе и первых прототипов Rust), инструментов статического анализа для ядерных электростанций, операционных систем для космических спутников и других ситуаций, требующих чрезвычайно высокой степени безопасности. Кроме того, это один из непосредственных предков Rust, F#, Elm, ReasonML и, если память мне не изменяет, первой версии React. А, и ещё Opalang. По какой-то причине конкурентность передачи сообщений позиционируется сегодня как «gochannels», но, насколько я знаю, она берёт начало ещё в Concurrent ML, одном из предков OCaml.
▍ Java
Я начал изучать Java, когда пытался модернизировать код, написанный на Turbo Pascal, чтобы он мог работать в Linux, Windows и System 7 (предшественнице macOS). Этот язык показался мне многословным, я не мог понять, почему разработчики не поставляют установщик, учитывая сложность выпуска и/или запуска классов, и мне очень не хватало дженериков, алгебраических типов данных и сопоставления с образцом. Однако на долгое время Java стал моим базовым языком — не таким, на мой взгляд, хорошим, как OCaml, но с доступом к более качественным библиотекам.
- Взрыв мозга: стандартная библиотека, в которой, похоже, есть всё необходимое мне!
- Взрыв мозга: документация, которую писали так, чтобы она была понятна пользователям!
- Взрыв мозга: JVML!
- Взрыв мозга: очень простой в использовании тулкит UX (даже если результаты разочаровывали)!
- Взрыв мозга: синхронизация как примитив языка (хоть это и не особо великолепная идея)!
- Взрыв мозга:
finally
! - Взрыв мозга: компиляция Just in Time!
- Взрыв мозга (в плохом смысле): понимание того, как
finally
реализовано в JVM.
В наши дни Java, разумеется — один из столпов отрасли. Также это один из предков C#, Scala и Go.
▍ Prolog
Если вы никогда не писали код на Prolog, то лишь рекомендую попробовать. Это полностью изменит ваше мышление о программах. В Prolog вы не пишете алгоритмы, а учите программу, как думать. Написать простой алгоритм поиска пути можно, задействовав примерно 2-4 тривиальных правила. Написание простой системы типов будет чуть длиннее.
- Взрыв мозга: учим программу думать!
Сегодня Prolog практически не существует. Однако его наследие огромно. Если память меня не подводит, SQL в большой мере основан на подмножестве Prolog с большим упором на оптимизацию в ущерб простоте. Система шаблонов C++ — это, по сути, более усложнённая реализация Prolog. Части системы типов Rust переписываются на Chalk — потомке Prolog.
Я подозреваю, что рано или поздно кто-нибудь начнёт комбинировать LLM с Prolog (или с Coq), чтобы создать поверх ChatGPT нечто надёжное. Кроме того, недавно я обнаружил TypeDB, очень похожий на новое поколение Prolog с API, напоминающим реляционные базы данных.
▍ Coq
Coq (и его родственники Twelf, Isabel и так далее) — это нечто совершенно иное. Вы кодируете спецификации целиком, как типы. Эти типы и есть ваша цель.
Вы программируете не с помощью функций и библиотек, а используя инструменты для преобразования ваших типов, чтобы постепенно упрощать их в независимые подцели. Можно описать это так: вместо кодинга вы решаете головоломку. Разумеется, подцели в результате превратятся в ваши функции, в ваши структуры данных и модули. Но произойдёт это почти случайно.
И, разумеется… модуль контроля типов настолько мощен, что ваша программа — это, на самом деле, формальное, математически проверенное доказательство её соответствия спецификациям. Множество математических теорем было доказано благодаря превращению их в код на Coq.
- Взрыв мозга: типы как спецификации.
- Взрыв мозга: программы как доказательства.
- Взрыв мозга: когда лемма проста, достаточно попросить Coq написать функцию за вас!
- Взрыв мозга: типы высших родов.
- Взрыв мозга: ожесточённые споры о том, какой же математик надёжнее, мозг или компьютер.
Я уже довольно давно не следил за разработкой Coq и других языков того же семейства. Насколько я понимаю, они довольно активно используются в научных кругах, где служат для формального доказательства соответствия спецификациям компиляторов и ядер операционных систем. Подозреваю, что если Prolog не вернётся, чтобы создать надёжные LLM, то кто-нибудь использует для этого Coq или Idris.
▍ Erlang
Erlang (а теперь и Elixir) был и остаётся лучшим языком для распределённых систем. Ядро гораздо проще, чем у Python, но легко масштабируется на миллионы узлов.
- Взрыв мозга: тривиальная реализация распределённых систем.
- Взрыв мозга: развёртывание нового кода из языка отправкой замыканий.
- Взрыв мозга: архитектура Let it fail.
Хотя многие всё ещё не слышали о Erlang/Elixir, этот язык использовался в отрасли начиная с 90-х в высокораспределённых системах. Его экосистема вполне жива, сейчас тестируется система статических типов. Модель let it fail применяется в мобильных операционных системах. Конкурентность и модели распределения распространились на Scala, Go и практически на всё остальное, где есть слова «микросервисы» или «акторы». На самом деле, меня печалит, когда я вижу микросервисы или Kafka, ведь по большей мере это повторные реализации архитектуры Erlang/Elixir, а также их внутренней BEAM Virtual Machine, но основанные на технологиях, которыми гораздо сложнее пользоваться и разворачивать, а также на протоколах, которые на множество порядков медленнее.
О, да, и Opalang тоже позаимствовал кое-что у Erlang.
▍ Пи-исчисление
Здесь я сжульничал. Пи-исчисление — это не язык программирования, а исчисление, то есть математическая модель, спроектированная для рассуждений о системах. Эту модель можно полностью определить всего в десятке строк спецификации и очень легко реализовать. Она использовалась для выполнения статического анализа моделей криптографических протоколов, протоколов банкинга… а также биологических процессов. Я использовал её для определения системы типов, которую разработчики теоретически могли бы применять, чтобы гарантировать правильную деградацию своих протоколов в условиях (D)DoS-атаки.
- Взрыв мозга: совершенно другой взгляд на условия гонки.
- Взрыв мозга: полнота по Тьюрингу любого вида последовательной операции не нужна.
- Взрыв мозга: описание сложных конкурентных/распределённых систем при помощи простой математики.
Я уже давненько не видел новых публикаций о пи-исчислении, но, возможно, просто не там смотрю.
▍ Opalang
Особый для меня случай, ведь я присоединился к команде и какое-то время был ведущим архитектором языка. Представьте, что вы пишете единую кодовую базу на простом функциональном языке + на основе акторов, сравнимом с OCaml/F#/ReasonML/Elm или с Erlang со статической типизацией, затем компилятор анализирует код на надёжность, безопасность и возможность распределённого использования, далее разделяет его на клиентский код (компилируемый в JavaScript + HTML + CSS), серверный код (компилируемый в нативное приложение) и код на стороне баз данных (компилируемый в схему базы данных и компилируемые запросы).
Я влюбился в этот язык. А затем выгорел, проектируя, разрабатывая и продвигая его в токсичном окружении. После этого мне понадобилось довольно много времени, чтобы вернуться в сферу проектирования языков программирования.
- Взрыв мозга: автоматизированное разделение на клиент, сервер и базу данных.
- Взрыв мозга: HTML со статической типизацией.
- Взрыв мозга: автоматическое устранение большинства проблем безопасности из Top 10 OWASP.
- Взрыв мозга: автоматическое распределение между веб-серверами (хоть мы и немного это переусложнили).
- Взрыв мозга: системы типов для анализа безопасности.
- Взрыв мозга: асинхронные волокна (fibers) в JavaScript (до
Promise
,async
/await
и воркеров DOM). - Взрыв мозга: база данных, ориентированная на структуры данных.
- Взрыв мозга: распределённая масштабируемая база данных (к сожалению, мы совершили ошибки, и на практике она оказалась довольно медленной).
Я считаю, что Opalang (и его родственники ReSharper и Ur/Web) был слишком большим прорывом для своего времени. Вся идея многоуровневого программирования, похоже, оказалась полностью забыта. Возможно, когда-нибудь она вернётся. WASM должен сильно упростить эту задачу.
▍ Rust
Потом я влюбился в Rust. Он сочетал в себе многие преимущества OCaml и Haskell, обещая дать разработчикам возможность писать высококонкурентный код и выполнять разработку на уровне систем. Кроме того, на тот момент революционными идеями были модульная сборка мусора и планирование M:N, хоть от этих идей и отказались до версии 1.0. В то время я начал экспериментировать с языком, по большей мере это был исследовательский проект. Помню обсуждения обработки ошибок, строк и итераторов. Один из важнейших пунктов в моём резюме: я один из тех, кто внедрил идею макроса
try!
, который затем преобразовали в оператор ?
.- Взрыв мозга: использование аффинных типов данных для доказательства свойств typestate.
- Взрыв мозга: писать код, по производительности сравнимый с C++, но с гарантиями уровня OCaml.
- Взрыв мозга: эффективная модель компиляции для
async
/await
. - Взрыв мозга: то, насколько успешно система типов приводит к безопасным конкурентным решениям.
- Взрыв мозга: помогающие сообщения об ошибках.
- Взрыв мозга: удобный процесс до RFC/RFC.
- Взрыв мозга: экосистема cargo и крейтов.
- Взрыв мозга: юнит-тестирование с принятием однозначной позиции (opinionated unit testing).
- Взрыв мозга: тестирование документации.
- Взрыв мозга: дружелюбное и приветствующее новичков сообщество.
- Взрыв мозга: частичная поддержка в написании моей собственной постепенной системы типизации (gradual type system).
У меня уже довольно долго не хватает времени, чтобы заниматься контрибьютингом в компилятор или stdlib, но когда оно найдётся, я планирую к этому вернуться!
К сожалению, мне кажется, Rust испортил для меня многие другие языки. Теперь когда я пишу на Python или Go (и тем, и другим я занимаюсь, чтобы зарабатывать на жизнь), или даже на TypeScript, мне всегда неприятна перспектива того, что мой код поломается, как только я его запущу. В Rust я кодирую в системе типов столько свойств, сколько могу, из-за чего перед написанием кода приходится немного дольше подумать, зато тесты обычно проходят с первого раза.
▍ А почему не $(ВАШ ЛЮБИМЫЙ ЯЗЫК)?
За свою карьеру я писал код на десятках языков программирования. В том числе на Haskell, Scheme, SmallTalk, (Tw)elf, Idris, Python, Go, PHP, Perl, Bash, C, C++, JoCaml,
JavaScript, TypeScript, Scala, C#, F#, Visual Basic, Ruby, (D)TAL, Squeak, Logo,
Scratch, UnrealScript, GDScript, Lua, SQL…
Если я не включил их в статью, значит, они не взорвали мой мозг. Это не значит, что эти языки не хорошие и не новаторские. Просто их концепция не вызвала во мне особого отклика или, что бывает чаще, я уже обнаружил те же концепции в каких-то других языках потому, что изучал их в таком порядке.
▍ Что дальше?
Я уже долгое время не видел ничего революционного, но это ничего не значит. Возможно, новая идея уже на подходе, где-то в небольшом исследовательском проекте, о котором я не слышал.
Когда я пришёл в PASQAL, то надеялся, что буду поражён языками для квантовых вычислений. Пока этого не случилось, но я не теряю надежды!
Мне кажется, что, несмотря на чрезмерный ажиотаж и безумный урон для окружающей среды, что-то полезное получится из ChatGPT и LLM, но пока не вижу этого. Я видел только интересные эксперименты, и многие игрушки уже кажутся поломанными. Любопытно, каким окажется будущее.
А какие языки повлияли на ваше мышление, связанное с программами?
Telegram-канал со скидками, розыгрышами призов и новостями IT ?
Комментарии (15)
smt_one
06.11.2024 13:11Эх, нет упоминания ATS. Надо действительно запланировать статью про него, а то про такую жемчужину на Хабре ничего и не видно.
ednersky
06.11.2024 13:11Взрыв мозга:
GOTO
!Очень хороший оператор. Мания удалять его из языков программирования - это победа сил зла над силами разума.
Moog_Prodigy
06.11.2024 13:11Меня всегда это удивляло. Ну давайте тогда и в асме отменим jmp, rjmp и прочие безусловные переходы. Как легко станет писать то сразу! Никаких переходов, никакой путаницы, исключительно условные операторы, условные переходы - красота.
nagayev
06.11.2024 13:11В асме пусть живет)
Все же если человек пишет в 2024 на асме он по идее точно знает что делает.
А вот на каком-нибудь JS не всегда.
ednersky
06.11.2024 13:11goto - идеальный оператор для написания, например, парсеров и вообще стейтмашин. при этом пофиг какой язык, если оператор goto есть, то читабельность кода в этом случае значительно выше, чем если этого оператора нет
nagayev
06.11.2024 13:11Понятно, что в каких-то случаях использовать GOTO нормальная практика. В ядре линукса например он используется в том числе чтобы реализовать что-то вроде деструктора - позакрывать там файлы при выходе из функции например. Но в целом для современных языков GOTO не очень нужен. GOTO прямо провоцирует нарушать правило одного выхода из функции (см. misra), которое не лишено смысла. Используя что-то современное вроде Python или C++ в отличие от Си у вас есть инструменты для этого вроде менеджера контекста with или идиома RAII, которую удобно реализовывать через деструкторы.
diakin
06.11.2024 13:11GOTO прямо провоцирует нарушать правило одного выхода из функции
Это как это?
nagayev
06.11.2024 13:11Ну потому что у вас есть возможность прыгать почти в любое место в коде (в С++ например нельзя обходить инициализацию переменной).
А имея просто return шанс что выход будет один все же выше.
А еще у goto есть другие недостатки: усложнение статического анализа например.
diakin
06.11.2024 13:11В смысле вплоть до из одной функции в другую? В этом случае программиста уже никакие парадигмы не спасут.
adeshere
06.11.2024 13:11Очень хороший оператор. Мания удалять GOTO из языков программирования - это победа сил зла над силами разума.
Кстати, а в каких современных языках, кроме фортрана, он реально используется (а не формально присутствует для обратной совместимости), и в каком обычно контексте?
Я застал древний фортран, где GOTO использовался на каждом углу, и читать этот код было невозможно. Даже когда прыжки GOTO строго вложены друг в друга, это уже нечитаемо, имхо. А если траектории прыжков пересекаются (например, со строчки N1 прыжок на N4, а со строчки N2 прыжок на N5), то это та самая катастрофа, попав в которую хочется вырезать все GOTO каленым железом, включая и их использование
в быту ;-)
Как известно, в русском языке такой оператор появился очень задолго до первых языков программирования и вообще вычислительной техники ;-)
P.S. Вдогонку сообразил еще один аргумент супротив неправильного употребления GOTO: совсем небольшая модификация этого простонародного оператора запросто может приводить (и во многих алгоритмах из позапрошлого века реально приводит)
к UB
... иди туда, не знаю куда ....
;-)
Сейчас я по-прежнему пишу на фортране, но немного в другом стиле: а именно, почти без GOTO. Однако, есть пара контекстов, где я не просто не избегаю GOTO, а наоборот, использую его, как стандарт. Наиболее типичный случай - это переход из тела функции в самый ее конец (обычно на стандартную метку 900) при обнаружении каких-то проблем в любом месте функции. Код после метки 900 анализирует локальный номер ошибки и ее контекст, формирует диагностические сообщения для юзера и для протокола, восстанавливает среду (например, функция могла открыть какой-то файл или аллоцировать память - файл надо закрыть, а память освободить) и устанавливает возвращаемое значение с учетом вида ошибки. Такая схема позволяет:
1) приблизить проверочный код в теле функции к тому месту, где проверяемые условия непосредственно задействованы, не разрывая логику функции. Так, входные параметры проверяются сразу при входе в функцию, а условный знаменатель - непосредственно перед делением на него. Это упрощает модификацию кода: проверочные операторы легко менять одновременно с исполняемыми;
2) При этом код, выявляющий нештатную ситуацию, занимает минимальное место и не мешает чтению общей логики (обычно он состоит из одного-двух IF-ов с последующим GOTO);
3) Весь код восстановления собран в одном месте, что позволяет избежать его дублирования после каждой проверки. Причем, он знает, из какого именно места произошел переход на метку 900, и с учетом этого выполняет все нужные операции в нужном порядке.Еще изредка у меня GOTO заменяет простой и короткий цикл WHILE с постусловием в тех случаях, ЕСЛИ
1) этот цикл практически всегда выполняется один раз (и лишь изредка надо его повторить два-три раза) И
2) запись постусловия в формате IF(...) GOTO улучшит читаемость кода.Я таким способом подчеркиваю для себя линейность алгоритма. Но имхо этот случай гораздо более спорный, чем описанный выше. Даже несмотря на обязательное требование к таким GOTO, что они должны вести не более, чем на несколько операторов назад, и что во внутреннем блоке не должно быть никаких ветвлений или условий.
vanxant
06.11.2024 13:11Очень хороший оператор
Соглашусь с комментатором выше. Вы просто не видели программ, построенных на базе пересекающихся goto. Какая-нибудь машина состояний. например. Я понимаю, что там это оправданно, и компилятор в итоге именно такую лапшу из goto (jmp, br, you name it) и выдаст. Но лучше пусть он этим и занимается.
Zenitchik
У автора, я смотрю, от каждой мелочи мозг взрывается...