четверг, 1 октября 2009 г.

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

Хочу развить комментарий тов.Skynin к заметке “Споры о языках программирования – не могу смолчать”:

...а я тут недавно сел, понадобилась утилитка работающая под "чистой" виндой, от вынь2к и выше, и что-то Си мне разонравился. Такие были хорошие воспоминания, и оказывается, на нем "крупу перебирать" хорошо, а не написать что-то непритязательное и быстро.
Тогда открыл VC++ - и вообще не понял ничего. Пока вывел строку wchar_t на экран...

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

На вершине холма стоят два быка – старый и молодой. Под холмом пасется стадо коров. Молодой бык говорит старому: “Давай мы быстренько-быстренько спустимся и вы*бем вон ту телочку!” Старый ему: “Нет…” Молодой опять: “Ну давай тогда мы быстренько-быстренько спустимся и вы*бем вон ту телочку!” Старый ему: “Нет…” Молодой снова: “Ну тогда давай мы быстренько…” Старый его прерывает: “Нет, мы сейчас спокойно, не торопять, спустимся, и вы*бем все стадо!”

Все молодые программисты, которых я видел, грешат одним и тем же: бросаются махать шашкой писать код не подумав толком что, как и зачем. Я и сам таким был. Это, наверное, нормальное явление – в молодости сил много, опыта мало, гормоны голос разума заглушают. У многих с возрастом и с проведенными в отладке и переделке ночами набитыми шишками это проходит. И это хорошо. Но сейчас я не об этом.

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

Первая группа не то, чтобы провоцирует быстрое программирование, но очень лояльно к нему относится. Это такие языки, как Pascal, Visual Basic, Java, Ruby, Python, D, Scala (наверное, в эту же категорию попадают и OCaml с F#, но не буду настаивать). Их характерными чертами являются:

  • отсутствие серьезных требований к знаниям языка программирования и его особенностей со стороны разработчика для получения работающего варианта программы;
  • отсутствие серьезных трудностей в поиске причин возникновения ошибок. Т.е., если программа, скажем, обратилась по нулевому указателю или вышла за пределы какого-то массива, то программист сразу об этом узнает – или в виде подробного стек-трейса, или в виде точного адреса, по которому элементарно определяется место возникновения ошибки. Т.е. эти языки бьют по рукам сразу и не больно;
  • многие из них предоставляют программисту возможность писать мало кода за счет различных видов “синтаксического” (и не только) сахара: автоматический вывод типов или динамическая типизация, foreach вместо циклов, лямбда-функции и пр.;
  • практически все они освобождают программиста от целого ряда проблем, как то: ручное управлению памятью, несовместимые компиляторы, разные системные API на разных платформах и т.п.;
  • для ряда из этих языков доступно большое количество уже готовых библиотек хорошего (на первый взгляд) качества на все случаи жизни. Иногда прямо в комплекте стандартной поставки.

Все это ведет к тому, что ужаленный в жопу загоревшийся идеей программист может сходу налабать что-нибудь работающее на Java/Scala/Ruby/Python, потратив 15 минут на изучение синтаксиса языка + 20 минут на чтение краткого описания библиотеки + 30 минут на набор текста и исправление нескольких ошибок (утрирую, но смысл точен). И, что характерно, решение получится действительно работающее. И не суть важно, что работающее медленно, отжирающее всю память, и с кучей постоянно возникающих ошибок. Важно, что результат есть.

Вторая группа языков (C и C++ в первую очередь) не препятствует ковбойскому быстрому программированию, но очень сильно и больно мстит программисту за это. Написали быстро программу с проходом по повисшему указателю или с выходом за пределы массива – получили периодические непредсказуемые падения и недели (в лучшем случае) увлекательной и изматывающей охоты за такими ошибками. Быстренько навставляли в программу классов из STL (стали возвращать std::string/std::vector и Co из функций по значению или передавать в параметрах по значению) – получился тормознутый и прожорливый монстр, много хуже, чем слепленный на Java аналог.

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

Ну и третья группа – это языки, которые не позволяют писать быстро. Они расчитаны на медленное, очень спокойное и вдумчивое программирование. В первую очередь, это язык Eiffel (а так же Ada, но на Ada я не программировал). Удивительно, но на Eiffel не получиться писать быстро (и мало) даже при очень большом желании. Тотальная объектная ориентированность + Design By Contract + принцип разделения command и query + особенности синтаксиса = мы сейчас спокойно, не торопясь, спустимся и…

Не знаю, будет ли это сюрпризом для читателя или нет, но при работе на Eiffel можно получать такие же минималистичные и эффективные решения, как на C++. При этом программист находится в твердой уверенности, что в его программе очень мало ошибок. Но кода будет написано на Eiffel-е, как минимум, в два раза больше, чем на C++.

Чтобы проиллюстрировать свою мысль о быстром и медленном программировании, покажу несколько примеров кода для решения одной несложной задачи. Вот ее решение на языке D 1.0 и на языке Nice (симпатичный язык для платформы JVM, по почивший к сожалению). Для людей, знакомых с C-подобными объектно-ориентированными языками в них не будет ничего особо сложного для восприятия. Обычный код. А вот, для сравнения, та же самая задача, но уже решенная на языке Eiffel.

Объем кода на Eiffel, как минимум, в два раза больше объема кода на других языках. Соответственно, и писал я его гораздо дольше (правда, нужно отметить, что большого опыта в Eiffel у меня не было и библиотеки у Eiffel-я специфические).

Такие вот три группы языков. Наверное, еще нужно сказать, что если язык допускает быстрое программирование, то при работе с ним возникает некий драйв/фан – ты спешишь переложить в код свои сумбурные мысли, код быстро пишется, программа быстро начинает дышать, ты чувствуешь ее пульс, ты видишь, как она отзывается на твои действия, это заводит тебя, ты распаляешься все больше… Ничего подобного нет в медленном программировании – там есть только скучное продумывание разных вариантов, учет всевозможных граничных условий, размеренное и неспешное кодирование с документированием… Рутина! Ну да, заработало с первого раза, ну да, делает то, что задумано, ну да, ошибки находятся раз в полгода… Но кайф-то где? ;)

Что еще характерно: по мере перехода из стадии “водка, лодка и молодка” в стадию “кино, вино и домино” мои языковые предпочтения серьезно меняются. Если еще лет пять назад я на Eiffel смотрел с ужасом, то сейчас думаю, что лучше было бы спокойно и не торопясь писать на Eiffel-е надежный код, чем пытаться бежать по минному полю делать это же, но на C++…

PS. Я не упоминал в тексте такие языки, как PHP, Perl, C# и Haskell, поскольку никакого опыта работы с ними у меня нет. Подозреваю, что PHP и C# точно попадают в первую группу. Perl, наверное, во вторую. Куда отнести Haskell – я даже не знаю.

Отправить комментарий