Мой комментарий

4.12.2009

Прочитав сегодня статью Пола Грэма "Языки программирования через сто лет", решил, во-первых, разместить ее на своем сайте, и, во-вторых, дать свои комментарии к этой статье.

Начну со следующей цитаты:

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

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

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

Очень давно (точно, до 1994 года) после знакомства с книгой Н.Вирта "Алгоритмы+структуры данных=программы" я попробовал написать простейший компилятор. Даже не написать, а переписать из книги. К тому моменту меня обуревал восторг от "персоналок" и от турбо-паскаля. Чтобы облегчить себе работу выкинул генератор кода, оставив только синтаксический анализатор выражений. Через некоторое время выкинул даже введение функций и т.п. В результате получился очень маленький вычислитель выражений. Около 150 строк исходного текста после компиляции превращались в программу менее 9Кб. После сжатия она становилась размером 6446 байт. Для работы с текстами потребовалась минимальная доработка.

После смерти ДОС-а я перестал программировать - последние 10 лет точно не программировал, даже все предыдущие навыки потерял. Конкурировать с молодыми бессмысленно, да и само программирование потеряло свою привлекательность. Заниматься изучением все новых и новых программных продуктов, чтобы выдать строку "Hello World" - что может быть глупее? Иногда компилировал на Delphi несколько строчек, иногда воодушевлялся игрушками типа сокобана, но, в целом, само программирование стало неинтересно. Осталась одна ниша - алгоритмы.

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

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

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

Понятно, что эта частная задача пересекается с темой рассматриваемой статьи. Взгляните с позиции этой частной задачи на строки:

Любой язык программирования можно поделить на две части: некий набор фундаментальных операторов, которые играют роль аксиом, и остаток языка, который, в принципе, может быть описан в терминах этих фундаментальных операторов.

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

Итак, язык. До Ф. Виета язык математики был не таким простым, к которому привыкли современные школьники. Да и его язык был еще очень сложным - мы должны благодарить Ф. Виета, прежде всего, за инициацию процесса упрощения языка. Не углубляясь в историю языка, отметим для себя несколько вещей.
  1. Именно языком человек отличается от прочих животных.
  2. Разнообразие языков говорит о некотором субъективизме при создании языка.
  3. Языки развиваются.
  4. Появляются специальные языки. Например, математика - язык современной науки.
  5. Язык математики становится мертвым, если из него выкинуть "обычный" язык.
  6. К языку можно привыкнуть и распространенность некоторого языка не может служить мерилом качества этого языка.
  7. Язык служит для передачи информации не только от человека к человеку, но и от поколения к поколению.
  8. Язык влияет на мышление человека, на его сознание.
  9. И т.д.
Возвращаясь к статье Грэма можно заметить, что само появление подобной статьи говорит о многом. Если характеризовать современное состояние языков программирования, то это состояние требует упрощения. После лавинообразного развития языков наступает момент перегруженности языков малопонятными конструкциями. С его характеристикой ООП трудно не согласится. Но опыт обычных языков говорит о том, что, каким бы трудным ни был китайский язык, он сохранится.

Маловероятно, что язык будет только один.

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

Раз имеется необходимость, а она имеется, то пробовать писать новые языки программирования обязательно надо.

Вернемся к частной задаче, к языку калькулятора TxCalc. Для вычислений можно было использовать и обратную польскую нотацию (язык Форт) и префиксные конструкции (язык Лисп). Были выбраны традиционные формулы, к которым все уже давно привыкли. Конечно, многоэтажные конструкции пришлось выкинуть, но ... "понятность" удалось сохранить. При выборе конструкций ветвления и циклов я стремился к минимуму (на самом деле, достачно было ввести один обобщенный цикл loop(), который может обслуживать и ветвление, но мне это показалось перебором). Почему я отказался от if-then-else, while do и т.д.? Нет, сами конструкции не так уж и плохи, не хуже предложенных. Но при обычном их применении имеется одна легко замечаемая пакость - наборы операторов, вставляемые в эти конструкции, часто по размеру превышают все разумные пределы. Программисты для борьбы с этой бедой используют отступы, но это часто не помогает.

Главным было введение "именованных областей". Понятно, что обойтись без нечто подобного нельзя. Процедуры, функции, подпрограммы и т.п. Неизбежность ввода чего-то "именного" была понятна и, самое главное, естественна (даже для непрограммиста). Оставалось выбрать обозначение. Я вспомнил фортовскую конструкцию:
: <имя> <набор операторов> ;
Так и сделал, но вскоре заменил её на более естественную:
[ <имя> <набор операторов> ]
Понятно, что это не принципиально.

Дальше просто ввел эти конструкции в интерпретатор - особых проблем не было, кроме одной. У меня активно используется конструкция для комментариев
{ комментарий }
которые выводятся в результат с заменой фигурных скобок на пробелы. А вот визулизация на этапе исполнения содержимого блоков и подпрограмм мне не нужна была. На это накладывается визуализация результатов конструкций типа x=?. Кстати, если вставить подобную конструкцию в блок [...], то при исполнении можно наблюдать "странные" вещи. Но... не до жиру - хорошо, что комментарии не выводятся.

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

Итак, начало положено. Нет ни одной случайной конструкции. Объеденены формулы и фортовская конструкция, значение которой не нужно объяснять. Остались непроработанными "данные". Фиксировать типы, как в паскале? Пока я склоняюсь к тому, чтобы типы использовать на этапе исполнения. В калькуляторе это не очень нужно, хотя использование комплексных чисел можно попробовать.

Недавно на форуме rsdn произошел следующий диалог:
>>>> [simp_int I=0 d=(b-a)/(2*n) x=a func I=y/2 x=b func I=I-y/2 x=a k=0 loop(IA,n-k,IB) I=I*(b-a)/(3*n)]
MC>Я вот вообще не могу вкурить, что тут за текст такой

Вот эквивалентный (и допустимый) текст:
[Интеграл_по_Симпсону
{задаем начальные значения}
I=0
d=(b-a)/(2*n) {размер интервала}
x=a
ФУНКЦИЯ {вычисляем y при x=a}
I=y/2 {понимаем, что оператор I=0 лишний, его можно выкинуть}
x=b
ФУНКЦИЯ {вычисляем y при x=b}
I=I-y/2
x=a
k=0 {индекс инициируем}
{Теперь вычисляем сумму в цикле}
loop(IA,n-k,IB)
{Умножаем сумму на необходимые коэффициенты для получения значения интеграла}
I=I*(b-a)/(3*n) ]

{Теперь все выше написанное можно записать и забыть. Пользуясь "поздним связыванием" для вычисления интеграла от некоторой функции задаем сначала функцию следующим, немного непривычным способом}
[ ФУНКЦИЯ {русские буквы разрешены} y=1/(1+x^2)]
{Задаем интервал a..b и число n}
a=0 b=1 n=12
{Вычисляем интеграл}
Интеграл_по_Симпсону
{Теперь выводим вычисленное значение с 10 знаками после десятичной точки}
I=?10
{УсЁ}
 
X