Про развитие программирования уже писано-переписано, и вряд ли можно сказать что-то принципиально новое. Однако полезно время от времени отрываться от текущих задач, окидывать взглядом прошлое и осознавать, как именно всё пришло в текущую точку. Легко воспринимать всё вокруг как данность, но когда разбираешься, понимаешь, по каким причинам что-то возникло. В истории было много витков, на каждом из которых языки программирования давали ответ каким-то запросам своего времени.
Этот пост — «краткое содержание предыдущих серий», где эти витки собраны вместе (конечно, в очень упрощённом виде: в одном тексте все важные нюансы не расписать). А после него, окинув взглядом весь контекст, можно и на текущие задачи посмотреть по-новому. Какие новые запросы человечества видны сейчас, и какими станут новые языки программирования, отвечающие на них? Расскажите в комментариях, через десять лет проверим.
Предыстория
В истории вычислительной техники тяжело найти точку отсчета. Что можно назвать первым вычислительным устройством? Счеты или набор камешков, с помощью которых выполняли простейшие математические операции? Кто ж разберет. Поэтому начнем наше повествование с арифмометров — сложных механических вычислительных устройств. Первый такой был создан в XVII веке; в начале XIX века началось их промышленное производство, а распространены они были вплоть до конца XX века.
Название устройства говорит само за себя: арифмометр производит арифметические операции с помощью системы из механических компонентов. В основном это разнообразные зубчатые колеса и цилиндры. Чтобы лучше представить принцип работы, можно ознакомиться с видео. В нем показано, как выполнялось сложение и вычитание на первом в мире арифмометре, изобретенном Вильгельмом Шиккардом в 1623 году.
Феликс — самый распространённый арифмометр в СССР
Следующая наша остановка — ткацкий станок Жаккара. Нам он интересен тем, что его, в определенной степени, можно назвать первой программируемой машиной. При работе с устройством использовались перфокарты, на которых записывались входные данные. С их помощью на станке можно было изготовлять ткани со сложными узорами. Именно перфокарты кодировали рисунок. Конечно, ткацкий станок — это не вычислительное устройство, но Жаккар внёс большой вклад в историю программируемой техники.
Станок Жаккара
В первой половине XIX века английский ученый Чарльз Бэббидж загорелся идеей создания аналитической машины — прообраза современного компьютера. На практике проект так и не был реализован, но только взгляните на его составляющие: арифметическое устройство, память, устройство ввода-вывода (кстати, с помощью перфокарт). Практически архитектура компьютера. Передовые идеи Бэббиджа не остались без внимания: Ада Лавлейс, переводя труды ученого, создала алгоритм вычисления чисел Бернулли на аналитической машине. Для прообраза современного компьютера существовал и прообраз компьютерной программы!
Ада Лавлейс — дочь английского поэта Джорджа Байрона
К концу XIX века начали появляться электромеханические вычислительные машины. Табулятор — одна из них. Это электромеханическая счетная машина, предназначенная для автоматической обработки информации. Первый табулятор был построен в 1890 году Германом Холлеритом для переписи населения США. В XIX веке на территории Штатов каждое десятилетие проводилась перепись и все подсчеты до появления табулятора выполнялись вручную. Такой подход не мог дать как быстрого, так и точного результата. Данные по переписи 1880 года были получены только через несколько лет, что делало их устаревшими. С новыми устройствами первые данные (по количеству населения) удалось получить примерно через месяц после проведения процедуры. Табуляторы, как и многие другие механические вычислительные устройства, были распространены до 70-х.
Первый табулятор IBM. Именно для серийного производства табуляторов была основана эта компания
К моменту появления первых ЭВМ была выработана мощная теоретическая база: теория алгоритмов, формализация вычислений и т. д. Но само понятие «программирование» окончательно сформировалось с появлением компьютеров и наложением теории на практику.
Языки программирования
Точка отсчёта: машинный код
Первые ЭВМ программировали с помощью машинного кода. Программа представляла собой набор команд, реализованных непосредственно на электронной схеме устройства. У разных ЭВМ перечень таких команд различался, но можно выделить общие моменты: арифметические операции, побитовые операции, обращение к ячейке памяти и т. п. При написании кода программисту приходилось ограничиваться такими элементарными операциями. Поскольку программа передавалась напрямую на процессор, она состояла только из того, что понимает процессор — набора чисел, обычно двоичных (существовали компьютеры и с другими системами счисления, например, «Сетунь»). По мере роста количества, объема и сложности программ работа с машинным кодом тормозила развитие ЭВМ. Также возникала нехватка специалистов: учиться работе с машинным кодом приходилось долго.
Набор машинных команд для советской ЭВМ «Минск-22»
Запоминаемость: ассемблер
Заложенных в процессоре команд практически никогда не хватало для решения сложных задач. Зачастую одни и те же фрагменты кода, например извлечение квадратного корня, кочевали из блокнота одного программиста к другому. Так сформировался определенный перечень часто используемых подпрограмм, которые из раза в раз приходилось реализовывать с нуля. Помимо этого оставался пресловутый двоичный код: сложно читаемый и сложно реализуемый.
Сформировалось две основные потребности:
- создание унифицированной библиотеки подпрограмм, которые автоматически настраивались и размещались в памяти;
- применение мнемоник в программировании.
Если с первым пунктом относительно всё ясно, то о чем идет речь во втором? Что такое мнемоники? Мнемоники — это приемы и способы, облегчающие запоминание информации. В прошлом, крестьянам, не знающим где «лево», а где «право», к ногам и рукам привязывали сено и солому. На левую ногу и правую руку — сено, а на противоположные части тела — солому. Крестьяне, вследствие особенностей своего образа жизни, знали разницу между соломой и сеном. Командиры использовали это, и вместо команды на марше «лево-право» отдавали команду «сено-солома». Так солдатам-невеждам было проще адаптироваться под армейские реалии и запомнить, где лево, а где право. Это пример мнемоники.
Мнемоники в языках программирования — это способ сделать код более легким для восприятия с помощью замены двоичных команд на их буквенные аналоги. Например, замена условной команды сложения «01» на «ADD». Или внедрение понятия «переменная» вместо адреса в памяти.
Первые реализации унифицированных библиотек с применением мнемоник были спроектированы в конце 40-х годов. Тогда, конечно, «ассемблерами» их никто не называл — первое упоминание этого термина всплыло в поздних отчетах к ЭВМ EDSAC. Сам по себе ассемблер — это не язык, а транслятор, переводящий команды в машинный код. А язык ассемблера, или assembly language — это язык программирования, который обычно подразумевают, когда говорят «писать на ассемблере». Как упоминалось ранее, каждая модель компьютера того периода имела собственные машинные команды, поэтому ассемблеры были строго специализированными для конкретных машин и на других не работали.
EDSAC
Портируемость: высокий уровень
Появление множества ассемблеров позволило упростить процесс программирования. Однако в этом крылась следующая проблема — их было много. Вычислительная техника развивалась, появлялось все больше и больше новых компьютеров, каждый из которых обладал собственным набором машинных команд и, как следствие, собственным ассемблером. С увеличением количества ЭВМ появилась потребность в более высокоуровневых языках программирования, на которых можно писать программы для разных машин. К тому же высокоуровневые языки повысили бы читаемость кода: ассемблер был куда проще двоичных команд, но всё же оставался сложным для восприятия.
Проблема портируемости решилась с появлением компиляторов и интерпретаторов. Конечно, оставалась необходимость создания таких программ для каждой модели ЭВМ, однако они позволили использовать один и тот же язык программирования для написания программ для разных компьютеров. Тогда к компиляторам относились с недоверием: примерно также, как и сейчас к коду, сгенерированному ИИ. Как это компьютер может сам составлять программу? Помимо этого, возникали опасения, что после компиляции программист не сможет понять исполняемых команд на машинном коде. Кстати, а что там с самими языками?
Грейс Хоппер — создатель первого компилятора
К концу 50-х годов появилось несколько языков высокого уровня:
FORTRAN, или FORmula TRANslation (1957) — язык программирования, созданный для сложных математических вычислений. Фортран вобрал в себя такие элементы, как циклы, условные операторы, массивы, подпрограммы.
LISP, или LISt Processing Language (1958), разработанный в MIT и изначально предназначавшийся для создания ИИ. Лисп был первым функциональным языком программирования. Наряду с Фортраном, Лисп используется и по сей день.
ALGOL 58 (1958) — «алгоритмический язык», оказавший большое влияние на будущее индустрии, а также послуживший источником идей для Pascal, C, C++ и т. п.
Читабельность: структурное программирование
Годы шли, и объем создаваемого кода неуклонно рос. С увеличением сложности программ все яснее вырисовывалась проблема: код должен быть не только работающим, но и читаемым. А с этим как раз имелись затруднения… Многие программисты писали слабо структурированные и запутанные программы, представляющие из себя линейный текст с обилием оператора безусловного перехода — GOTO. Такое явление получило название спагетти-кода.
Пример спагетти-кода на Бейсике: печать чисел от 1 до 10 и их квадратов
Как говорится, время — деньги, а часы, потраченные на блуждание по лабиринту из GOTO (профессионалов особо раздражал переход назад) в попытках прочесть код, явно не были продуктивными. Многие специалисты ратовали за методологию структурного программирования — применение управляющих конструкций (последовательность, ветвление, цикл), разбиение на подпрограммы и отказ от GOTO. В пользу такого подхода имелась теория: в 1966 году итальянские математики Коррадо Бём и Джузеппе Якопини доказали теорему о том, что любой алгоритм можно представить в виде трех структур управления: последовательной, ветвлений и циклов. Через два года известный программист Эдсгер Дейкстра использовал этот научный результат в судьбоносной статье Go To Statement Considered Harmful («О вреде оператора goto»), название которой говорило само за себя. Новую парадигму поддержали многие авторитетные специалисты, но далеко не все. Например, Дональд Кнут до сих пор не согласен с тотальной отменой GOTO.
Но что насчёт самих языков программирования? Некоторые ключевые идеи структурного программирования появились задолго до статьи Дейкстры. Так вложенные блочные структуры и лексическая область видимости были представлены еще в семействе языков ALGOL. В 1970 году Никлаус Вирт, ушедший из комитета по ALGOL 68, создал «эталонный» структурный язык Паскаль, изначально предназначавшийся для обучения, но снискавший определенную популярность (к данному моменту язык едва подает жизнь в Delphi). В 1972 году на свет появилось довольно специфичное детище Денниса Ритчи — язык C. Специфичное во многом из-за того, что его конструкции были сопоставимы с машинными командами. Из-за этого язык как Ленин — живее всех живых. То, что находится в интимной связи с железом (ядра операционных систем, драйвера, прошивки микроконтроллеров), до сих пор работает на С.
Помимо статьи о GOTO, Дейкстра известен своим алгоритмом поиска кратчайшего пути на графе и изобретением «семафоров» для синхронизации процессов в многозадачных системах
В целом дебаты о GOTO, разгоревшиеся в 60-е и 70-е, закончились тем, что некоторые новые языки и реализации существующих не включали пресловутый оператор безусловного перехода в явном виде, что в какой-то степени навязывало новую методологию. Хорошо это или плохо — вопрос философский. Несомненно то, что структурное программирование стало основой практически всего, что появилось дальше.
Масштабируемость: ООП
60-е и 70-е были знаменательны отнюдь не только становлением структурного программирования. В эти годы новые парадигмы появлялись как грибы после дождя. Правда, в основном в стенах университетов и лабораторий, где ученые экспериментировали с разными методологиями. Вот некоторые интересные языки, разработанные в то время.
Prolog (1972) — один из первых логических языков программирования, модификация которого использовалась в программном обеспечении космического корабля «Буран».
ML (1973) и Scheme (1975) — функциональные языки программирования, основанные на Lisp; ML был примечателен полиморфной системой типов Хиндли—Милнера, а Scheme своим минималистичным синтаксисом и применением в знаменитом учебнике SICP, который долгие годы являлся базовым учебником по программированию в MIT.
В языках Simula (конец 60-x) и Smalltalk (середина 70-x) развивались идеи объектно-ориентированной парадигмы.
В 1978 году появился SQL, который впоследствии стал основным языком запросов к реляционным базам данных.
Космический корабль «Буран». ПО для бортового компьютера и операционная система были написаны на ПРОЛ2 — модификации Prolog
Начиная с 80-х годов все больше вырисовывалась потребность в программировании больших систем с использованием модулей или крупномасштабных организационных единиц кода. И объектно-ориентированный подход для этого отлично подходил благодаря удобным методам моделирования предметной области. Также ООП хорошо сочеталось с событийно-ориентированным программированием и разработкой графических интерфейсов, которые появились примерно в то же время. Среди языков, появившихся в 80-е, можно выделить: Ada (1983), созданный по контракту с Министерством обороны США и используемый в крупных государственных проектах; С++ (1980), разработанный Бьерном Страуструпом в качестве объектно-ориентированного расширения языка С; Objective-C (1984), созданный Брэдом Коксом и Томом Лавом для их собственного стартапа Stepstone и впоследствии лицензированный Стивом Джобсом для разработки операционных систем Apple (macOS, iOS). ООП оказалось настолько удачным апгрейдом процедурного программирования, что и по сей день прикладные языки, поощряющие или допускающие объектно-ориентированный подход, превалируют над другими языкам.
Эпоха интернета: скриптинг
Быстрый захват мира интернетом в середине 90-х стал одним из крупнейших событий в истории программирования. Радикально иная платформа для компьютерных систем открыла двери для новых языков программирования. Именно тогда начали набирать популярность скриптовые языки для специализированных веб-приложений, которые до сих пор находятся в топе: JavaScript (1995), взлетевший благодаря интеграции с браузером Netscape Navigator; нелюбимый кодерским комьюнити PHP (1995), что не мешает ему быть востребованным. Интересно, что произошел довольно любопытный феномен — интерпретируемые языки, часто применяемые для скриптинга, постепенно превратились в языки общего назначения, позволяющие разрабатывать довольно широкий спектр ПО. Взять хотя бы ныне очень популярный Python (1990), на котором можно и веб-приложение создать, и задачу по анализу данных решить и много чего еще. В связи с этим, усилилось влияние функциональной парадигмы, расширяющей возможности по созданию высокоуровневых абстракций.
Вторая крупная тенденция заключалась в том, что огромные объема кода требовали инструментов быстрой разработки. Поэтому основное внимание с языков сместилось к инфраструктуре: мощные IDE, сборка мусора, фреймворки и богатые библиотеки. К таким языкам можно отнести Visual Basic (1991), Java (1995), Delphi (1995), C# (2001).
В целом новейшая эпоха показала нам одну очень важную вещь. Насколько бы ни был хорошим язык, ключевую роль в востребованности играет его поддержка и создание на основе языка целой платформы. Java прекрасно это иллюстрирует: сам язык может и не быть идеальным, но богатый выбор инструментов для решения широкого ряда задач (многопоточное программирование, кроссплатформенная разработка, работа с базами данных, сетевое взаимодействие и т. д.) обеспечили языку популярность и своеобразное долголетие.
Текущие потребности: что дальше?
Фукуямовский конец истории вряд ли грозит IT-сфере: эволюция языков программирования продолжается как в промышленности, так и в исследованиях. Есть целый ряд тенденций, которые могут сказаться на языках — от повышения интереса к филосософии открытого исходного кода и до исследований в области применения ИИ для генерации кода.
Так что, вероятно, нас ещё ждут новые языковые парадигмы, которые ответят на запросы нового времени. Как думаете, какими они будут? Есть ли у вас запросы, на которые языки пока что не ответили?
Комментарии (11)
permeakra
08.04.2022 22:57-1Simula и Smalltalk - не совсем ООП. К современному его пониманию они не имеют вообще никакого отношения. Simula, при всей её фундаментальности, создавалась с достаточно узконконретной задачей - имитационное дискретное моделирование (Discrete Event Simula-tion)
anka007
09.04.2022 00:25+1Описаная эволюция скорее не самих языков программирования, так как тут каждый сам за себя в своей области эволюционирует, а методов формализации знаний о мире. Какие слова и значки используются не настолько важно, как сам подход к формальному описанию решения задачи. Пока создали два независимых подхода: формализация через объект и формализация через функцию. На мой взгляд следующий уровень отойдет от строгого описания алгоритма, программист будет переводить в формальное описание новым языком программирования не решение задачи, а само условие задачи для того, чтобы исполнитель типа ИИ искал ее решение. Но чтобы начать ставить задачи таким образом надо создать то, что будет ее решать.
А в рамках существующих подходов эволюция идет в область расширения сфер применения, соотвественно все большее число функций и объектов надо описывать. Сначало это поиск наиболее удобного и быстрого способа формализации новой сферы с созданием нового языка, а потом попытки создать один инструмент, позволяющий решать любую поставленную задач. И вот здесь можно будет заметить эволюционные изменения в рамках одного языка (Примерно как менялись FORTRAN, C, C++, Java)
shashurup
09.04.2022 07:19+1Язык программирования, в первую очередь, это язык коммуникации одного программиста с другим. Коммуникация программист - компьютер имеет значение, но, чем дальше, тем все более второстепенное. И именно из-за этого языки программирования будут развиваться по законам в чем то сходным с законами развития естественных языков. Так же как в естественных языках мы "коллективно договариваемся" о новых словах для обозначения новых явлений, типовые решения для задач замеченные по мере накопления опыта появляются в виде новых синтаксических конструкций.
kernelapi
09.04.2022 13:08Если пофантазировать, то вполне может быть, что дальнейшее развитие языка будет сочетаться с нейросетями и позволит уйти от конкретного синтаксиса и даже языка для большинства задач, описывая логику работы программы, а не конкретный код. А конкретный код мог бы в рамках описанного генерироваться сеткой, подобной AlphaCode.
В этом смысле весь процесс развития инструментов выглядит как путь от машинно-ориентированного языка на человеко-ориентированный. Вместе с ростом мощности вычислений и сложности это вполне разумный и даже необходимый переход.
rsashka
Обработка больших данных и тензорные вычисления на уровне базового синтаксиса языка, а не с помощью использования дополнительных библиотек.
Это нужно для того, что бы "больше времени" тратить непосредственно на алгоритм, а не на написание текста для вызова различных оберток над данными.
saipr
Это зависит от того как хорошо или профессионально написана обёртка. Возьмите любой скриптовый язык, например, упоминающиеся в статье JavaScript, PHP, Python, или мною уважаемый tcl (tcl/tk), ведь без наличия в них широкого спектра пакетов/балалаек/модулей, т.е. тех же обёрток, они были бы мало интересны.
rsashka
А я имею ввиду не качество реализованной обертки, а многословность и сложность написания того или иного алгоритма с её помощью. Ведь если смотреть на историю развития программирования, то можно увидеть, что постоянно повышается уровень абстракции, которыми оперируют люди при разработке вычислительных машин и алгоритмов.
Сперва, реализация непосредственно в железе, потом, машинные коды, далее ассемблер, языки высокого уровня, ООП ... И с каждым этапом добавляются новые возможности, а старые становятся лишними и неудобными для использования.
Сейчас мейнстрим - тензорные вычисления, но нет языков, которые их поддерживают из коробки на уровне синтаксиса. И это отвлекает от решения основной задачи, т.к. приходится работать с обертками на старых языках программирования, синтаксис которых разработан еще в прошлом веке.
saipr
Но это же и зависит как раз от того как написана обёртка. Кстати, всё это относится и к языкам. Что такое повысить уровень абстракции, ведь его кто-то должен обрабатывать, а это может быть и транслятор и компилятор и препроцессор и интерпретатор и библиотека!
Так может для начала переработать обёртку, чтобы она была из нашего века, а не из прошлого?
rsashka
Потому что изначальных синтаксис языка это не предполагает, поэтому и приходится использовать обертки.
Я думаю, что новые парадигмы программирования, это оперирование тензорами или даже целыми понятиями. А возможно и создание языков программирования на основе каких нибудь регулярных человеческих языков для прямого общения между машиной и человеком.
permeakra
Это называется APL. Из более модерновых - Julia