понедельник, 29 декабря 2008 г.

Программирование и рисование

В очередном RSDN-новском флейме была высказана аналогия между программированием и рисованием:
Рисовать можно научить любого. Научить же передавать через рисунок свои чувства (а тем более, чувства других людей), практически нереально, этому необходимо учится самому, имея некоторый врожденный фундамент и желание/стремление.
С утверждением о том, что рисовать можно научить любого, я, как несостоявшийся художник-самоучка, категорически не согласен. В процитированной мной фразе вообще есть внутреннее противоречие – по-моему, человек умеет рисовать только тогда, когда он рисунком может передать свои впечатления (будь то пейзаж, натюрморт или портрет). А то временами бывает, что все черты переданы в точности, а портрета нет – поскольку нет искорки в глазах или же какой-то характерной, неуловимой для зрителя черты – потому что художник не смог передать то самое впечатление. Тем не менее, данная аналогия хороша, если сравнивать обучение рисованию/живописи и программированию. Итак, начать нужно с того, что рисованию и программированию можно научить только тех людей, которые наделены природными способностями. Для рисования это какая-то особенность нервной или двигательной системы (а может их обеих в комплексе), благодоря которой рука художника точно исполняет команды мозга, в результате чего и формируется изображение. Плюс к этому чувство дистанции и перспективы. Для программирования нужна врожденная способность разбивать какое-то простое действие на произвольное количество мелких и еще более простых последовательных составляющих. Это у обычного человека операция рисования окружности с помощью циркуля выглядит одной неделимой операцией – берешь циркуль и рисуешь. А у программиста это целая серия шагов – поднести циркуль к линейке для выбора радиуса, раздвинуть ножки циркуля на нужное расстояние, поднести циркуль к нужному месту на бумаге, воткнуть ножку с иголкой в бумагу, повернуть циркуль на 360 градусов вокруг ножки с иголкой. Т.е., если природой способностей к рисованию или программированию не дано, ничего путного не получится. А если дано? А если дано, то процесс обучения – это, во многом, процесс тренировки, натаскивания. В рисовании на уровень инстинктов выводится владение, скажем, техникой штриховки. В программировании – способность написания циклов или разбиения одной большой функции на несколько мелких. А то и вовсе – способность набирать тексты в редакторе. И, если речь идет только о технике, то можно сказать, что вышколить любого имеющего природные способности человека до виртуозного владения техникой, в принципе, возможно. Это только вопрос времени. Хотя, нужно отметить, что на обучение программированию тратится гораздо меньше времени, чем на обучение рисованию. Ну да здесь можно сделать скидку на то, что в рисовании очень важна физическая тренировка – рука сама собой должна точно повторять нужные движения (штрихи, мазки). В программировании такое совершенное взаимодействие "мозг-рука" не требуется. Но техника (что в рисовании, что в программировании) не определяет успех усилий автора. Если мы рассматриваем какое-то художественное произведение, то мы рассматриваем три вещи: 1. Сюжет (идею). 2. Композицию (расположение фигур на рисунке или картине). 3. Исполнение (технику рисунка). Как я сказал выше, технике можно натаскать. Композиция, в принципе, так же может быть донесена до ученика в процессе обучения. Поскольку существуют яркие примеры того, что называется "правильной" композицией, "неправильной" композицией, "перегруженной" композицией и т.д. Хотя и здесь есть интересный феномен – иногда люди могут очень грамотно и красочно рассказывать теорию композиции, но на практике им не удается создавать удачные композиции в своих произведениях. Так что здесь так же есть фактор природных способностей – у кого-то чувство композиции заложено изначально, ему достаточно нескольких ярких примеров для того, чтобы подсознание выдавало готовые композиции. У остальных таких способностей нет. Но они могут пользоваться имеющимися примерами как образцами. А вот с сюжетом ситуация наиболее интересна. Я не знаю, можно ли как-то тренировать креативность, развивать способность выдумывать сюжеты. И сильно сомневаюсь в том, что это возможно. У меня всегда были проблемы с выдумыванием сюжетов. Подсмотреть готовый сюжет (особенно, если речь идет о пейзажах или портретах), это запросто. А вот родить что-нибудь вроде "Атомной Леды" или "Сон, навеянный полетом пчелы вокруг граната за миг до пробуждения", или "Предчувствие гражданской войны" Сальвадора Дали – уже нет. Не дано. И не обучиться этому.
Вот так же думаю, дело обстоит и в программировании. Когда речь идет о программной системе, мы можем рассматривать некоторые аналогичные вещи: 1. Поход к решению задачи. 2. Архитектура системы. 3. Качество кода. Качество кода, это как техника в рисовании. Можно, в принципе, надрессировать любого разработчика писать качественно. Вопрос только во времени. Если у программиста есть способности. Архитектура системы – как композиция. Если мы говорим об объектно-ориентированном программировании, то можно вспомнить паттерны. Которые можно рассматривать как примеры "хороших" композиций. Только вот у некоторых разработчиков паттерны автоматически распознаются и реализуются на уровне подсознания. Другие же способны перечислять названия паттернов, их характерные черты и особенности, но не способны удачно использовать их в своих программах. А вот подход к решению задачи, это как сюжет у рисунка. Можно позаимствовать его у кого-то. Иногда сама задача диктует подход к своему решению, почти как пейзаж диктует художнику, что именно он должен нарисовать. Однако мне очевидно, что у разных программистов совершенно разные способности к выбору подходов для решения задач. Во многом, думаю, способность к выбору правильного подхода определяется врожденными способностями. Развитыми со временем, но все-таки врожденными. Какой же из всего этого потока сознания вывод? Как и в рисовании, в программировании многое зависит от природных способностей. Которые обязательно должны быть развиты упорным трудом. И совокупность этих двух факторов (способности и вложенные в их развитие усилия) определяет качество программиста. А посему, действительно классные программисты – это такая же редкость, как и действительно классные художники.

пятница, 26 декабря 2008 г.

Why FP doesn't matter yet? ;)

В последнее время в определенных кругах наблюдается повышенный шум по поводу функционального программирования (ФП). В одном из обсуждений ФП подкинули ссылку на статью Why Functional Programming Matters. Меня, как завзятого скептика по отношению к ФП статья не впечатлила. Уж сильно обсуждаемые там примеры далеки от того, чем мне приходится заниматься. Но, в попытке разобраться с примерами кода из статьи я хорошо понял, почему ФП, не смотря на свой почтенный возраст, до сих пор широко не распространено. А вот почему:
easydiff f x h = (f(x+h)-f x) / h

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

easydiff(f, x, h) = (f(x+h)-f(x))/h

Глядишь, и народ бы к ним потянулся :)

четверг, 25 декабря 2008 г.

Программистам нужны простые решения?

В своем интервью Don Syme (главный разработчик языка программирования F#) сказал, что сейчас программистам нужны простые решения:

People thirst for simplicity. People need simple solutions to the problems that really matter: data access, code development, deployment, cloud computing, Web programming, and parallel programming, to name a few.

Будучи сам программистом, я не могу согласиться с таким утверждением. Мне не нужны простые решения. Простое решение, это, например, язык Oberon. Ну и где он, кто им пользуется? Даже Java и C#, которые начинались как гораздо более простые инструменты, чем C++, сейчас по сложности или приближаются к нему, или уже превзошли (в частности, C# 3.0 и готовящийся к выходу C# 4.0). Программистам нужны мощные решения. А мощность и простота редко идут рядом. Что действительно нужно, на мой взгляд, так это многослойное решение, которое можно изучать и применять, постепенно переходя от простого к сложному, как бы снимая слой за слоем. При этом обеспечивается легкое и быстрое получение первых результатов. По сути, человек быстро и незаметно подсаживается на иглу. После чего он вынужден либо изучать инструмент более тщательно, либо менять инструмент. Для достижения такой многослойности, как мне кажется, нужно придерживаться нескольких правил (все нижеследующее относится к инструментам, которые действительно делают что-то полезное): 1. Решение (инструмент) должно быть построено на основе небольшого количества простых идей и понятий. Например, SObjectizer в своей основе имеет только агентов с их состояниями, сообщениями и событиями. Все остальное, это надстройка над ними. 2. Решение (инструмент) должно быть снабжено хорошими обучающими материалами и документацией. Понятие "хороший", конечно же, субъективно. Но, например, изучать Ruby по книге "Programming Ruby" гораздо проще, чем C++ по книге "The C++ Programming Language". А все из-за того, насколько по-разному в каждой из книг построена подача материала – в одной идет плавное погружение человека в новый для него язык, а во второй – практически мгновенный переход к деталям. Поэтому что-то вроде Tutorial или Developer Guide с последовательным изложением тем от простого к сложному, это must have. При несомненной важности подобного обучающего руководства обязательно должно быть что-то вроде полного описания всех его классов, функций и пр. -- подробный Reference Manual. Поскольку очень обидно, когда после прочтения Tutorial-а пытаешься сделать что-то свое и негде найти список параметров какого-то метода. 3. Инструмент, если речь идет о более-менее серьезной библиотеке, должен иметь API нескольких уровней. Верхний уровень позволяет очень просто выполнять наиболее общие или наиболее востребованные действия. На этом уровне широко используются умолчания и пользователь, как правило, не имеет возможности делать какие-то тонкие настройки. Например, если какая-то операция полагается на использование пула потоков (thread pool), то пользователь не может указать какой именно пул потоков должен использоваться, сколько в этом пуле должно быть потоков, с каким приоритетом они должны работать и пр. Как раз такой верхний уровень API призван обеспечить легкое получение первых результатов у новых пользователей. Хороший пример – это параметры таких шаблонов, как std::vector, которые имеют значения по умолчанию. Вот, скажем, есть у std::vector такой параметр, как Allocator. А подавляющему большинству C++ программистов никогда не приходится его определять самостоятельно. Под верхним уровнем должно быть несколько более низких уровней, каждый из которых требует от программиста все лучшего и лучшего знания инструмента и понимания того, чего же программист хочет достичь. Но при этом программисту предоставляется все больше и больше возможностей. Ярким нарушением этого принципа, мне кажется, является библиотека Poco. В ней, к примеру, есть классы ActiveMethod и ActiveResult, которые используются для реализации фьючерсов. Но указать ActiveMethod, на каком именно пуле потоков должен выполняться асинхронный вызов метода нельзя. 4. Решение (инструмент) должно быть снабжено большим количеством примеров разнообразных способов его использования. Качественных примеров. Ведь зачастую фрагмент из примера посредством copy-and-paste переносится в код пользователя. Или же пример используется в качестве "рыбы" для программы пользователя. Обидно, когда библиотека содержит только примеры вида "Hello, world", либо не содержит примеров вовсе – в качестве таких примеров пытаются выдать unit-тесты. Хотя, например, в случае с ACE распространяемые с ACE тесты довольно часто оказываются вполне хорошими примерами и серьезно помогают во время изучения очередной возможности ACE. Но это, скорее, исключение. Не так уж и мало всего перечислил. Что, на мой взгляд, еще раз подчеркивает одно житейское наблюдение – не бывает простых решений сложных проблем. И для того, чтобы сделать что-то, что создает впечатление простого, нужно приложить немало усилий. Как замечательно сказал Алан Перлис:
Simplicity does not precede complexity, but follows it.
(источник).

среда, 24 декабря 2008 г.

Профессиональные убийцы времени

В программировании многое очень быстро меняется. Поэтому нужно стараться держать нос по ветру и отслеживать свежие течения. Но, в ряде случаев этот процесс отнимает гораздо больше времени, чем хотелось бы. В последнее время меня раздражают две вещи, речь о которых пойдет ниже. Во-первых, это RSS-ленты. В самом RSS, как в инструменте, ничего плохого нет. Плохо, что среди потока новостей находится много "информационного мусора". На одно действительно хорошее и ценное сообщение (чаще всего это анонсы каких-то инструментов, реже ссылки на полноценные статьи) приходятся десятки ссылок на чьи-то блоги с еще одним мнением на какой-то фундаментальный вопрос. Особенно часто сейчас это наблюдается на тему Concurrent и Parallel Programming. Какой-нибудь очередной Вася Пупкин открывает для себя Erlang, Actor Model или OpenMP и тут же строчит опус (благо, если короткий) в свой блог. После чего ссылка на сие творение распространяется через RSS-ленты. А я трачу 5-10 минут времени только для того, чтобы понять, что ничего нового здесь для себя не открою. Еще одна вариация убийства времени за счет RSS-лент, это когда в течение короткого времени на каком-нибудь достойном сайте появляется куча статей и статеек, рассказывающих об одном и том же, но разными словами (в лучшем случае). Так, недавно в Dr.Dobb's появилось штук пять или даже больше статеек на тему Intel Threading Building Blocks. Хотя, на мой взгляд, вполне хватило бы и одной-двух, но чуть более полных. Во-вторых, хотелось бы к профессиональным (в смысле имеющим отношение к профессии программиста) убийцам времени причислить видеоматериалы, которые все больше и больше используются для распространения информации о новых продуктах и технологиях. У Microsoft-а есть Channel9, где периодически появляются интервью с разработчиками новых продуктов и презентации этих продуктов. На Google Video часто выкладывают различные подобные материалы. После проведения различных конференций доклады скорее появляются в виде видеозаписи выступлений докладчиков, чем в виде слайдов или стенограмм. Для меня все эти видеоматериалы плохи тем, что они слишком длинные. И еще хорошо, когда рассказ сопровождается демонстрацией кода и результатов его исполнения/отладки (в свое время меня поразило, насколько информативными бывают небольшие по размеру screencast-ы с демонстрацией возможностей Ruby-On-Rails или редактора TextMate). Но когда речь докладчика всего лишь сопровождает некоторой несущественной информацией то, что демонстрируется на слайдах того же докладчика, то становится обидно за потерянное время - тот же самый объем информации можно было бы получить, потратив десять минут на чтение стенограммы доклада, чем за час просмотра этого же доклада. Мне кажется, что оба эти убийцы времени происходят от одного и того же - от халтурного отношения и от спешки. Выступает кто-то в Channel9 у Microsoft, зачем делать стенограмму его выступления? Достаточно просто опубликовать запись. Появилась где-то очередная заметка о том, насколько, оказывается, хорош асинхронный обмен сообщениями - в RSS-ленту его. Вне зависимости от того, стоит эта заметка вообще чьего-либо внимания или нет. Быстрей, быстрей, думать некогда! Нужно быстрее новую порцию интеллектуальной жвачки пропихнуть, пока пипл хавает.

понедельник, 22 декабря 2008 г.

Так ли хорошо использование мейстрим языков программирования?

Больше десяти лет я программировал на самом мейнстримовом языке программирования последних двадцати лет - C++. И не сильно задумывался, хорошо это или плохо. Однако, по большому счету, мейнстримовым программистом я все-таки не был, т.к. изрядное количество времени я работал на экзотических платформах (например, OS/2 или OS-9000), и занимался не самыми типичными задачами (АСУ ТП, работа со SmartCard-ами через PC/SC ридеры, поддержка телекоммуникационных протоколов вроде SMPP и EMI). Познакомившись за последние несколько лет с целым рядом языков, которые не являются мейнстримом (например, D, Scala, Eiffel, Nice, Erlang, Ruby, OCaml), я стал более критично рассматривать полезность следования мейнстриму. В частности, на примере языков программирования.

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

Итак, чем же хорошо и чем плохо следование мейнстриму при выборе языка программирования?

Для компании использование мейнстримовых платформ хорошо тем, что, во-первых, нет проблем с наймом программистов: раз это мейнстрим, значит, есть достаточное количество людей, владеющей этой платформой. Во-вторых, нет проблем ни с инструментарием, ни с документацией, ни с техподдержкой. Для мейнстрим платформ существует большой выбор самых разных инструментов (компиляторов, профайлеров, IDE, библиотек и пр.). Огромное количество книг и on-line ресурсов, даже специальных обучающих курсов. Очень большое сообщество, в котором, при желании и настойчивости можно найти ответ на любой вопрос. В-третьих, мейнстрим либо означает возможность работать на большом количестве платформ (как пример кросс-платформенность Java), либо же работать на большом количестве компьютеров (как пример распространенность OS Windows, под управлением которой работает больше 95% всех персональных компьютеров в мире).

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

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

Теперь о том, какие риски есть у мейнстрима.

В первую очередь рассмотрим риски для компании.

Начать можно с того, что существует мнение о том, что каждые 10 лет появляется новый доминирующий язык программирования. Вроде того, что с 1986-го по 1996 -- это был C++, затем с 1996 по 2006 -- Java, с 2006 -- таковым MS хочет сделать C#. Так же меняются "горячие штучки" в самой индустрии. В 70-ые годы -- мейнфреймы. В 80-е -- развитие персоналок в одной нише и сетевых ОС (в первую очередь Unix-ов) в другой. С 1990-го по 2000-й было развитие desktop-систем (как раз в это время Windows стала доминирующей ОС). С конца 90-х до недавнего времени -- WWW, Web-системы и Web-программирование. Так же где-то с 2000-го активно развиваются ниши встраиваемых систем и мобильных систем (проще говоря, программирование для мобильников).

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

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

Так же это означает, что если компания собирается выходить на рынок с новым продуктом, то правильный выбор будующего технологического лидера очень важен. Не очень разумно было вкладываться полностью в Java в 96-99-ых годах. Не очень разумно было вкладываться в C++ в 2004-2008-ых годах. Но, если с перспективной технологией угодать и начать ее использовать за пару лет до ее выхода в мейнстрим, то за счет этого можно получить чисто технические преимущества. Ведь каждая новая технология устраняет часть проблем, которая присутствовала у предшественников. Скажем, C++ добавил к C поддержку ООП. Java добавила к C++ безопасность, сборку мусора и дешевую кроссплатформенность. C# добавил к Java родную поддержку Windows и элементов функционального программирования. Т.е. переходя на новые языки/платформы, разработчики получают конкурентное преимущество перед теми, кто использует "старые" технологии.

Вот, например, сейчас стало модно обсуждать multicore процессоры. Оказывается, что очень сложно добится серьезного увеличения производительности программы при переходе на 2-х, 4-х и более ядерные процессоры. Часто наблюдается даже обратный эффект, на нескольких ядрах программа работает даже медленнее, чем на одном. Во многом в этом виноваты существующие подходы к разработке программ и существующие мейнстримовые языки программирования в частности. Появление новых языков, более приспособленных к параллельному программированию, или же встраивание специальных возможностей в существующие языки сможет дать серьезное преимущество при разработке хорошо масштабируемых программ.

Еще один аспект мейнстрима для компаний. Как известно, самое важное слагаемое успеха это люди. Несколько "правильных" людей способны производить больше качественного кода, чем несколько десятков(!) "неправильных" (по наблюдениям ДеМарко и Листера, да и не только их, разница в производительности между хорошими и плохими программистами может составлять до 10 раз). Но "правильных" людей, т.е. талантливых, увлеченных программистов, зачастую интересует то, что находится "на грани", что еще не попало в мейнстрим. Многие из них хотят применять то, что они считают перспективным и интересным. Поэтому, если дать им такую возможность, то это забавным образом повлияет на возможность найма людей: предложений на рынке будет гораздо меньше, зато качество предложений будет гораздо лучше.

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

Чем же плох для разработчика мейнстрим?

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

Так же программисту нужно узнавать что-то новое и, желательно, не только в том, чем он занят на работе. Зашоренность всегда плоха. Тем более, что в программировании есть несколько совершенно разных ниш, каждая из которых требует своего инструмента. Например, для каких-то мелких, одноразовых задач хорошо подойдут скриптовые языки (типа Python или Ruby). Для desktop-приложений -- C++ (особенно для мелких, легких программ), C# и, отчасти, Java. Для server-side -- Java, C# и динамические языки вроде Erlang, Python, PHP и Ruby. Для встраиваемого ПО -- C, C++, Java, Ada. Для высоконадежного ПО с высокими требованиями к ресурсам и производительности -- C++, Ada, Eiffel. И т.д. Получается, что чем большим количеством хороших инструментов владеет программист, тем проще ему будет выбрать подходящий инструмент для какой-то из своих задач (если, конечно, у программиста есть выбор).

Sic transit gloria mundi :)

Вот и достигнута широкая известность в узких кругах! ;) После четырех лет активного участия в RSDN-овских баталиях меня сочли достойным кандидатом на включение в очередной вариант RSDN-новского гимна (я там в качестве персонажа E). Пустячок, а приятно ;) Ну и если уж хвастаться, то на данный момент я нахожусь на шестом месте в общем Top-100 всего RSDN, и на первых местах в форумах Философия программирования, C/C++ прикладные вопросы и Языки с динамической типизацией (за счет статьи о языке Ruby). Жалко только, что этот рейтинг на хлеб вместо масла не намазывается :)