суббота, 31 октября 2009 г.

[comp.prog.thoughts] Мои крамольные мысли о будущем языков программирования

Нижеследующий поток сознания навеян просмотром темы “Язык будущего. Какой он?” на сайте RSDN. Я не согласен с тем, что будущее языков программирование лежит где-то в области хардкорного функционального программирования с зависимыми типами (там дали ссылку на почти 50-страничный документ Dependent Types At Work, в котором нет (на первый взгляд) ни одной практически-значимой задачи, типичный академизм). Так же далекое будущее не связано с освоением multi/manycores платформ – по сути, это уже идет сейчас. Так что же требуется от языка программирования будущего?

Начну, согласно литературным канонам, издалека. С апреля месяца я работал над серией проектов (не очень связанных между собой), практически в авральном режиме. Требовалось делать все очень быстро, поскольку сроки были буквально “вчера”. И мне приходилось постоянно сообщать свои прогнозы руководству. Естественно, первые прогнозы никогда не оправдывались. В лучшем случае я ошибался в 1.5-2 раза. Т.е. заявлял срок в один месяц или в 10 дней, на практике же получалось два месяца или 15 дней.

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

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

Продемонстрирую это на примере. Стоит задача написать приложение, которое будет подключаться к POP3-серверу, забирать письма, фильтровать уже обработанные, выдергивать из необработанных писем нужную информацию и отсылать ее дальше. Предварительный прогноз (после того, как была написана тестовая программка для проверки возможностей класса POP3ClientSession из библиотеки POCO) – три-четыре дня. Но ушло почти шесть дней. Поскольку первые мысли были приблизительно такие: большой цикл, в начале открытие POP3-сессии, получение списка писем, последовательный перебор писем, выделение или генерация для каждого письма Message-ID, проверка уникальности Message-ID по БД, выделение нужной информации из письма, передача получателю, фиксация Message-ID в БД, удаление письма с POP3-сервера, периодическая чистка БД, а если долго не было нормальных POP3-сессий или ответов от получателя, то завершение работы.

В принципе, ничего сложного. Вот, скажем, последний пункт: если долго не было нормальных POP3-сессий или ответов от получателя, то завершение работы. Казалось бы, что в этом сложного. Делается за 15-минут, ну за полчаса. Но во что все это реально выливается на практике?

Во-первых, ограничение времени отсутствия нормальной работы должно конфигурироваться. Значит, в структуру cfg_t вносится еще одно поле, m_max_time_without_successful_receive. С комментариями, с объяснением назначения этого поля и определением его значения по умолчанию.

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

Далее, если разбор конфигурационного файла контролируется unit-тестами (а по хорошему должен контролироваться), то следует прописать в unit-тестах несколько test case для проверки обработки этого значения.

Далее, в классе агента-читателя почты нужно завести метку времени последнего успешного чтения почты, в виде атрибута m_last_receive_timestamp. С комментариями, с объяснением назначения этого атрибута.

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

Далее, нужно выделить в коде места, в которых будет проверяться, не слишком ли долго не было успешного чтения почты. Если таких мест несколько, то…, а если нет, то…

Далее, нужно решить, как именно будет логироваться факт завершения работы программы из-за превышения времени. Сообщение лога должно быть специальным образом описано и задокументированно.

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

Далее, в код агента добавляется обновление мониторов m_last_pop3_session_result и m_time_from_last_receive.

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

Вот теперь все. На практике эта работа у меня заняла что-то около 2.5-3 часов рабочего времени. 3 часа рабочего времени – это практически половина рабочего дня (даже больше, если брать в расчет разные производственные и непроизводственные разговоры с коллегами). Т.е. за полдня удается сделать одну не очень значительную детальку не очень сложной программки. А таких деталек… И ведь никогда не известно их точное количество – всегда по ходу разработки на свет божий выбираются такие невероятные детали, о которых изначально даже не задумывались. А время идет.

Какое отношение все это имеет к будущим языкам программирования? Прямое :)

Программист делает двойную работу. Первая часть его работы – составление детального плана реализации какой-то возможности. Что-то вроде приведенного выше перечня абзацев, начинавшихся со слова “Далее”. Это очень трудоемкая работа, поскольку здесь идет прокручивание в голове разработчика какой-то мысленной модели решаемой задачи. По возможности полной модели. С учетом различных тонкостей и нюансов.

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

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

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

Произойдет ли это когда-нибудь? Я не знаю, да и не уверен в этом.

PS. Все вышесказанное является моим субъективным имхом. Причем сегодняшним. Завтра все может поменяться и я буду думать по другому :)

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

2 комментария:

Miroslav комментирует...

ну это ж "семантический разрыв". Я о нем узнал в универе лет 11 назад, а существует он с самого начала.
радуйтесь, что работаете в неформальной среде :)
Вот тут с юмором обсуждаются предложения по LeanIT и тп. веяниям в корпоративной среде http://geekandpoke.typepad.com/geekandpoke/2009/10/lean-it.html
Кроме того между "девеловер сделал билд" и "билд попал к кастомерам с их ценными данными" нужна целая великая китайская стена :( иначе кастомеры могут начать нервничать.

eao197 комментирует...

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