oldnewlang-addons

языки, которые мы потеряли\\ (врезка)

крис касперски, а.к.а мыщъх, a.k.a nezumi, a.k.a elraton, a.k.a souriz, no-email

В Лиспе (Lisp) и Форте (Forth), созданных в 1958 и 1970 годах соответственно, самомодификация была вынесена на уровне языка, что позволяло реализовывать высокоэффективные программы, построенные на динамических алгоритмах. Уникальнейшей особенностью Форта (не реализованная ни в каком другом языке) была и остается возможность «честной» модификации Форт-машины, то есть непосредственно самого транслятора, который при желании со стороны программиста можно вообще полностью переписать штатными средствами самого языка!

В Фортране, Алголе, Паскале, Си/Си++ и прочих «хороших и разных» клонах (включая Бейсик) самомодификация возможна лишь теоретически — путем варварской правки машинного кода в оперативной памяти. Почему «варварской»? Да потому, что _язык_ к этому не имеет никакого отношения, более того, самомодификация опирается на недокументированные возможности языка, закладываясь на логику конкретных трансляторов, что чревато развалом программы при переходе на другой компилятор, не говоря уже о том, что машинный код — штука понятная лишь небольшому кругу избранных, но даже Великий Гуру не сможет написать самомодифицирующуюся программу, работающую более чем на одном процессоре.

В конечном счете, самомодификация попала в черный список дурных приемов программирования, а сами программы «распались» на код и данные, обрабатываемые этим кодом, инвариантным по отношению к самим данным. Другими словами — один и тот же код обрабатывает разные данные, что не есть правильно. Машина Тьюринга вообще не имела таких понятий. В ней код был неотделим от обрабатываемых им данных. Вернее, поступающие данные задавали методы их обработки (естественно, в рамках заложенных в машину алгоритмов).

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

Во-вторых, Си++, Nemerle и R# поддерживают (и активно продвигают) парадигму метапрограммирования, позволяя писать программы, создающие другие программы, которые в свою очередь создают третьи… Это, конечно, не совсем самомодификация, но нечто на нее похожее. Однако, реализация метапрограммирования крайне тяжеловесна, логика и синтаксис — запутаны, сложны для понимания и абсолютно непрозрачны, а возможности существенно уступают Форту и Лиспу. В общем, муть и мрак.

oldnewlang-addons_image_0.jpg

Рисунок 1 Джон Мак-Карзи (John McCarthy) — создатель Лиспа

oldnewlang-addons_image_1.jpg

Рисунок 2 Чарльз Мур (Charles Moore) — создатель Форта

Объектно-ориентированное программирование — просто модный термин, за которым не стоит ничего (за исключением денег и рекламы). Многие языки, возникшие задолго до появления термина ООП (например, уже упомянутый Лисп), вполне отвечают его критериям, возможно, даже в большей степени, чем «чистокровные» представители ООП, такие как Си++ или его прямые предшественники Симула (Simula) и Смолток (Smalltalk), созданные в 1967 и 1980 году соответственно.

Кстати, о предшественниках. Язык Си++ часто критикуют за то, что обращение к методу класса в нем реализовано как вызов функции, в то время как Симула и Смолток позволяли объектам обмениваться сообщениями. Сторонники Си++ утверждают, что конкретный способ вызова метода класса — внутренняя кухня языка, скрытая от программиста и что посылка сообщения является частным случаем вызова функции, только прямой вызов намного более эффективен в плане накладных расходов и машинных затрат на такты и оперативную память.

Все это так, но… Симула и Смолток естественным образом поддерживают многозадачность, многопоточность и распараллеливают вычисления на уровне языка. Посылка сообщений может быть как синхронной (объект посылает сообщение и ждет результата), так и асинхронной (объект посылает сообщение и продолжает заниматься своими делами). Это позволяет транслятору в кооперации с операционной системой равномерно раскидать объекты по процессорам или даже по целому кластеру. Конечно, содействие со стороны программиста крайне желательно (не все алгоритмы допускают эффективное распараллеливание), но в любом случае, программа, написанная на Симуле или Смолтоке, _автоматически_ увеличивает свою производительность при добавлении новых процессоров.

Прямой вызов функции, напротив, всегда синхронен. Обращаясь к методу класса, текущий код передает ему бразды правления и получает их только после явного возврата, который может вообще никогда не произойти! Распараллеливать программу приходится на этапе ее создания, причем, реализовать динамический алгоритм, поддерживающий произвольное количество процессоров, в общем случае невозможно или крайне затруднительно, т. к. для этого фактически потребуется вручную реализовать Симула/Смолток-подобный «движок», натянув его поверх Си++, затратив кучу усилий и получив в итоге худшую производительность чем на чистой Симуле (Смолтоке).

Вплоть до настоящего времени этот недостаток Си++ никого не беспокоил, поскольку многопроцессорные компьютеры встречались крайне редко, да и то большей частью на серверах, одновременно обрабатывающих множество запросов от пользователей и распараллеливание велось именно в этом направлении — один и тот же Си++ код выполнялся на нескольких процессорах одновременно, каждый экземпляр которого обслуживал «своего» пользователя и все были довольны.

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

Рисунок 3 эмблема Smalltalk'а