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

[life.photo] Фоторепортеры – кому война, а кому…

Фото из WSJ Photo of the Day:

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

Затем я представил, как могла происходить съемка вот этого кадра:

один плачущий ребенок на Суматре – вокруг с десяток фоторепортеров…

Но, с другой стороны – до нас впечатления о жестокости и ужасах Великой Отечественной дошли благодаря работе фото- и кинооператоров. Тогда ведь так же могли быть случаи, когда вокруг плачущего ребенка стояло несколько фотографов и каждый из них старался сделать наиболее выразительный кадр (с учетом того, что фотографов в те времена было на порядки меньше). Со стороны работа фотографа на войне тогда так же выглядела дико. Зато теперь мы имеем возможность видеть что это такое.

Вспоминается история, которую я когда-то слышал по телевизору. Пожилой фотограф рассказывал, как много лет назад его с коллегами привезли на место крушения самолета. Они должны были снимать произошедшее. Но никому из фотографов не удавалось сделать больше нескольких снимков – происходившее вокруг настолько потрясало, что невозможно было сосредоточится на съемке. Люди убирали фотоаппараты и начинали помогать собирать останки погибших. А на тех, кто появлялся с фотоаппаратом и начинал снимать они сами чуть не бросались с кулаками, потому что не могли понять – как все это можно было спокойно фотографировать…

пятница, 9 октября 2009 г.

[comp.prog.flame] Более конструктивные мысли после двух суток выкуривания gSOAP

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

Довольно своеобразная штука этот gSOAP. С одной стороны, очень удобно: берешь WSDL, натравливаешь на него wsdl2h, затем soapcpp2 и вуаля – в твоем распоряжении набор классов для обращения к удаленной стороне (ну или для реализации собственного сервиса).

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

class SOAP_CMAC PPGw__Sms : public PPGw__Request
{
public:
   int dataCodingScheme;
   std::string *fromNumber;
   xsd__base64Binary *header;
   PPGw__PremiumInfo *premiumInfo;
   std::string *protocolIdentifier;
   std::string *reportLevel;
   std::string *toNumber;
   LONG64 validityPeriod;
public:
   ...
            PPGw__Sms() :
         dataCodingScheme(0),
         fromNumber(NULL),
         header(NULL),
         premiumInfo(NULL),
         protocolIdentifier(NULL),
         reportLevel(NULL),
         toNumber(NULL),
         validityPeriod(0) { this->soap_default(NULL); }
   virtual ~PPGw__Sms() { }
};

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

void
do_send_sms( PPGwBindingProxy & proxy )
   {
      std::cout << "...sending sms..." << std::endl;

      std::string baID = "***";
      std::string text_sms_selector = "TextSms";
      std::string text_of_message = "Just a Test!";
      std::string to_number = "+420***";
      std::string msg_id = "c0465d63-38f0-4f31-93b3-0dc80a063baf";

      PPGw__TextSms text_sms;
      text_sms.text = &text_of_message;
      text_sms.dataCodingScheme = 0;
      text_sms.toNumber = &to_number;
      text_sms.baID = &baID;
      text_sms.msgID = &msg_id;

      PPGw__MessageContainer mc;
      mc.selector = &text_sms_selector;
      mc.textSms = &text_sms;

      PPGw__send request;
      request.mc = &mc;

      PPGw__sendResponse response;

      int error_code = proxy.send( &request, &response );
      if( error_code )
      ...

А во втором случае (чтобы память почистил за нас gSOAP), вот такой:

void
do_send_sms( PPGwBindingProxy & proxy )
   {
      std::cout << "...sending sms..." << std::endl;

      PPGw__TextSms * text_sms = soap_new_PPGw__TextSms( &proxy, -1 );
      (text_sms->text = soap_new_std__string( &proxy, -1 ))->assign( "Just a Test!" );
      text_sms->dataCodingScheme = 0;
      (text_sms->toNumber = soap_new_std__string( &proxy, -1 ))->assign( "+420***" );
      (text_sms->baID = soap_new_std__string( &proxy, -1 ))->assign( "***" );
      (text_sms->msgID = soap_new_std__string( &proxy, -1 ))->assign(
            "c0465d63-38f0-4f31-93b3-0dc80a063baf" );

      PPGw__MessageContainer * mc = soap_new_PPGw__MessageContainer( &proxy, -1 );
      (mc->selector = soap_new_std__string( &proxy, -1 ))->assign( "TextSMS" );
      mc->textSms = text_sms;

      PPGw__send * request = soap_new_PPGw__send( &proxy, -1 );
      request->mc = mc;

      PPGw__sendResponse response;

      int error_code = proxy.send( request, &response );
      if( error_code )

      ...

Как по мне, так и первый, так и второй варианты – это страх и ужас, особенно второй.

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

А кроссплатформенных альтернатив gSOAP-у под C++, в общем-то, и нет. Была какая-то реализация SOAP-а от Apache (Apache Axis C++) – но там не видно обновлений с 2005-го года. Еще у разработчиков библиотеки POCO есть какой-то собственный POCO Remoting. Но он сильно дороже gSOAP-а, не умеет (как я понимаю) транслировать WSDL в C++, да и вообще его способности поддерживать именно SOAP слегка под вопросом (поскольку там для SOAP-а есть только некий SoapLite).

В связи с этим появляется соблазн взять и склепать собственный wsdl2cpp, который бы из WSDL генерировал заточенные под POCO или Qt нормальные C++ные классы. И продавать бы такой инструмент эдак по $200 за версию + upgrade на следующую версию за $75. Ей Богу, была бы такая альтернатива gSOAP-у – выбрал бы сейчас ее не задумываясь.

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

[comp.prog.flame] Человеконенависнические мысли после суток выкуривания gSOAP-а

Изобретатели XML-я – редиски.

Те, кто догадался использовать HTTP для взаимодействия программ – редиски.

Те, кто догадался использовать HTTP+XML для взаимодействия программ – редиски вааще.

Авторов SOAP-а павбывавбы.

Тех, кто догадался использовать SOAP для доступа к сервисам мобильных операторов вместо нормальных SMPP и UCP – павбывавбы! Павбывавбы медлено и мучительно, с особым цинизмом.

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

[comp.prog.thoughts] Инструмент SPARK выходит под GPL

Недавняя новость: компании Praxis и AdaCore выпускают SPARK Pro под GPL-ной лицензией. SPARK – это специальное подмножество языка Ada (существуют версии SPARK для Ada-83, Ada-95 и Ada-2005) и набор инструментов для верификации и доказательства корректности написанных на SPARK программ. Предназначен для разработки систем с чрезвычайно высокими требованиями к корректности.

Недавно миру было предъявлено публичное доказательство убойности инструмента SPARK: проект Tokeneer. Это исследовательский проект для разработки ПО биометрической идентификации: система должна была управлять входом сотрудников в защищенное помещение. Насколько я помню, в ее состав входил сервер для хранения сертификатов с биометрической аутентификацией и ПО для взаимодействия с биометрическими датчиками и для управления дверью помещения. В результате написанное ПО работало в режиме имитации – вместо разных датчиков и прочей реальной лабуды работали программные имитаторы.

В процессе разработки были выполнены следующие фазы:

  • анализ требований;
  • формальная спецификация (с использованием специального языка Z);
  • проектирование;
  • реализация на SPARK;
  • верификация с использованием инструмента SPARK Examiner;
  • тестирование системы.

Разработка заняла 260 дней, было написано 9939 строк кода. После поставки системы (я так понимаю, после тестирования, уже для дальнейших экспериментов) в ней было выявлено 2 ошибки. Эксперимент был признан удачным (не зря же им теперь размахивают как победным флагом).

Не берусь судить о том, насколько все это круто (все-таки всего 10K строк кода за почти год работы нескольких человек)… Ну да они там в своем монастыре, у них свои уставы.

Я о другом хочу сказать. Вот это веяние – выпуск продуктов бесплатно под GPL – это же не от хорошей жизни, имхо. Сначала EiffelStudio стали раздавать под GPL. Теперь вот SPARK (GPS – среда разработки под Ada и GNAT-компиляторы Ada уже давно под GPL ходят, насколько я знаю). Думаю, что это свидетельство того, что данные инструменты никому не нужны. Тем более за те деньги, которые за них просят. Вот и пытаются их владельцы исправить ситуацию – мол мы вам первую дозу бесплатно, только попробуйте, зацените кайф…

Дело тут, наверное, в том, что выросло целое поколение программистов (может быть не одно), которое о языках типа Ada и Eiffel даже не слышало. Даже на уровне названий. Это в начале 90-х об этих языках еще говорили. Вот, мол, Ada. На нем американские военные свои системы пишут. Или Eiffel, который с Design By Contract, и на котором системы жизнеобеспечения, и встроенные системы разрабатывают. Потом пришло время Web-а, Java, C#, массового аутсорсинга и понятия “индусский код”. Ни Ada, ни Eiffel на эту платформу не вскочили (да и не должны были по статусу). А в результате они оказались на пороге забвения (имхо).

Кому сейчас нужны эти старички? Когда вокруг такой hype вокруг волшебных свойств функционального программирования. Зачем браться за какой-то там Ada-подобный SPARK, если можно тоже самое сделать на Haskell-е и получить и корректность, и волшебное распараллеливание, и еще кучу всего хорошего и вкусного?

Так что, похоже, сейчас есть возможность понаблюдать за вымиранием динозавров (Ada и Eiffel). Не знаю, достигли ли они стадии immortality, как COBOL. Вероятно, достигли. Так что совсем они не исчезнут, но останутся для многих современных программистов такой же фольклорной страшилкой, как COBOL и Fortran.

среда, 7 октября 2009 г.

[comp.prog.help] Какая книга помогла вам в освоении многопоточного программирования?

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

Помнится, мое знакомство с многопоточностью произошло где-то в 1995-1996 на платформах Windows NT и OS/2. Основной информацией была документация по Win32 и OS/2 API, исходники и примеры какой-то Borland-овской библиотеки, плюс базовые знания о параллельном программировании (семафоры, тупиковые ситуации) из университета, плюс попытка изучения языка Ada, в котором модель параллельного программирования несколько иная.

Потом была отличная книга Джеффри Рихтера (что-то типа вот этой книги, но года 1995 издания) о программировании в Win32 и книга Теренса Чана о программировании на С++ под Unix (насколько я помню, она не сильно меня впечатлила, но познакомила с POSIX Thread).

Все, что было позже – это уже было повторение пройденного. До недавних пор, когда много стали говорить о lock-free программировании и использовать низкоуровневые конструкции, вроде spin-lock-ов. Но на тему современного низкоуровневого программирования есть здоровенный толмуд под названием The Art Of Multiprocessor Programming (к своему стыду, я ее так и не дочитал, уж слишком низкий уровень она описывает в самом начале).

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

вторник, 6 октября 2009 г.

[comp.prog.sobjectizer] Идея об использовании mbox-ов в ядре SO5

Очередная идея для SObjectizer-5: использование почтовых ящиков (mbox-ов) для адресации всех сообщений.

Там же можно посмотреть на описание одного и того же объекта с использованием данной идеи и в текущем SObjectizer-4. Сокращение кода агента почти в два раза. Что мне очень понравилось. Пока это лучшее из того, что мне удавалось придумать для SObjectizer-5.

[comp.prog.concurrency] Презентация Гайя Стила “The Future Is Parallel”

67-страничная презентация “The Future Is Parallel: What’s a Programmer to Do?” от Гайя Стила.

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

В конце презентации идут выводы, который, на мой взгляд, и не требовали демонстрации в виде малопонятных с первого раза примеров.

Зато фрагменты кода на Fortress выглядят очень прикольно. На их фоне Lisp кажется еще более write-only языком, чем он есть :)

Update. Появилась еще одна презентация на эту тему от того же автора: Organizing Functional Code for Parallel Execution: foldl and foldr Considered Slightly Harmful. Содержание практически такое же, как в первой презентации. Чуть изменилось оформление, в середину добавился еще один пример. В выводах упомянута та самая фраза из заголовка о foldl и foldr (хотя в основном тексте их и не вспоминали). Появились ссылки на какие-то новые работы по автоматическому распараллеливанию. В общем, если кто-то не смотрел первую презентацию, можно заглянуть сразу во вторую.

Update #2. А вот, вроде бы, и видео для второй презентации (сам не смотрел).

[life] Видео: подготовка к продаже отчужденного за долги дома

Не могу не высказать того, чего не понимаю. Ну то, что народ набрал кредитов на дома за $400K не задумываясь о возврате кредита -- это обычное дело. А вот то, что выезжают из дома, оставляя там телевизоры, мебель, бытовую технику, компьютеры и пр. -- вот это для меня загадка. Ведь есть же у них некоторое время до выселения. Неужели нельзя было по дешовке все это барахло распродать? Глядишь, лишняя тысяча-другая долларов на жизнь оказалась бы в руках. Или дела там настолько плохи были, что даже плазменные телевизоры и за $100 некому было продать?

понедельник, 5 октября 2009 г.

[comp.prog] Void safety в языке Eiffel

Примечание. Все описанное ниже является моим вольным пересказом статьи Avoid a Void: The eradiction of null dereferencing. Так что, если в моем тексте что-то не понятно, то милости прошу в первоисточник :)

Итак, в недавно вышедшей версии 6.4 продукта EiffelStudio (компилятор языка Eiffel + среда разработки) реализованы изменения в языке Eiffel, которые позволяют устранить проблему обращения по нулевым ссылкам. В языке Eiffel нулевая ссылка – это значение Void. Отсюда и название – Void safety. Изменения в языке Eiffel обеспечивают гарантию того, что обращение вида x.f или x.f(…) будет невозможным для нулевых x.

Обеспечивается это за счет следующих вещей:

  • введением понятия attached и detacheable ссылок;
  • введением понятия Certified Attachment Patterns (CAP) – специальных конструкций языка, которые гарантируют ненулевое значение ссылки;
  • введением специальных конструкций языка – object test-ов, check-инструкций и stable-атрибутов.

Пойдем по порядку. Eiffel – это язык со сборкой мусора. Поэтому практически все пользовательские объекты – это динамически созданные объекты (за исключением т.н. extended-типов, но с ними просто – для них не может быть ссылок, только значения), а доступ к объектам осуществляется через ссылки. Для ссылок ранее было разрешено значение Void (аналог NULL в C-подобных языках). Более того, ранее Void было значением по умолчанию для ссылок.

Сейчас, по умолчанию, ссылки не могут быть нулевыми. Т.е. объявление ссылки вида:

r: MY_TYPE

обозначает, что r не может принимать значения Void (т.е. NULL). А компилятор не позволяет обращаться к r до ее инициализации:

some_method is
  r: MY_TYPE
do
  r.f -- Здесь теперь возникнет ошибка компиляции.
end

Ссылку, которая не может принимать значения Void, можно так же объявить с помощью модификатора attached:

some_method is
  r: attached MY_TYPE
do
  create r
  r.f -- Этот вызов корректен, т.к. ему предшествует инициализация.
end

Если же нужна ссылка, для которой значения Void разрешены, то она объявляется явно как detachable:

r: detacheable MY_TYPE

По умолчанию, все локальные переменные, параметры методов и атрибуты объектов являются attached ссылками. Что, по мнению разработчиков языка, существенно повышает безопасность программирования. А язык берет на себя контроль за тем, чтобы обращение к attached ссылке не происходило без ее инициализации (т.е. присваивания начального значения локальным переменным и атрибутам класса, передачи не-Void значений в качестве аргументов методов).

Но вот полностью избавиться от detacheable ссылок невозможно, т.к. значение Void вполне естественно для целого ряда структур данных и алгоритмов. Т.о. требуется механизм, который бы позволил реализовать контроль того, что detacheable ссылка переводится в attached состояние только не будучи нулевой.

Первой частью этого механизма является CAP (Certified Attachement Patterns) – конструкции языка, которые гарантированно обеспечивают отсутствие Void в переменной-ссылке. Такими CAP служат проверки на равенство/неравенство Void:

x /= Void and then ...
x = Void or else ...

Такие конструкции гарантируют, что в действиях, которые стоят на месте многоточия, ссылка x не может быть нулевой (только если x не является атрибутом объекта, но об этом ниже).

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

some_feature (a: detacheable MY_TYPE) is
  require
    a /= Void
    a.f /= 0 -- это обращение к f() безопасно.
do
  ...
end

Здесь обращение a.f во втором предложении контракта гарантированно безопасно, поскольку первое предложение – это CAP, который проверил не равенство Void-у.

Но одних CAP-ов недостаточно, поскольку они не распространяются на атрибуты объекта. А не распространяются потому, что в условиях многопоточности значение атрибута может измениться между двумя соседними точками программы. Например, пусть t – это атрибут класса DEMO. Тогда следующая конструкция не обеспечивает void safety:

if t /= Void then
  t.f -- здесь могут быть проблемы!
end

Поскольку после проверки атрибута t в if-е, вторая нить может вытеснить первую, и изменить значение t. Чтобы этого не происходило, значение t нужно скопировать в новую локальную переменную. И проверять на равенство Void уже эту локальную переменную. Эти действия в языке Eiffel объединены в специальную конструкцию, называемую object test. Вот как это выглядит:

if attached t as l then
  l.f -- здесь обеспечивается Void safety.
end

Еще одной конструкцией, для работы атрибутами объекта являются т.н. stable атрибуты. Если атрибут в классе помечен как stable, то он не может получать значения от detacheable ссылок и выражений. Т.е. контролируется, что если stable-атрибут используется в левой части присваивания, то в правой части присваивания обязательно будет attached значение. Что позволяет использовать stable-атрибут без дополнительных проверок. (Впрочем, данная возможность даже авторами статьи объявляется неоднозначной и, возможно, со временем она видоизменится).

И последняя специальная конструкция для работы с ссылками для обеспечения Void safety является конструкция check. В предыдущих версиях Eiffel она была чем-то вроде assert-а в C-подбных языках и, если не ошибаюсь, могла деактивироваться при компиляции в release-режиме. Для поддержки Void safety эта инструкция получила новую форму:

check attached x as l then
  ... -- какие-то действия над l
end

Т.е. проверяется, равно ли x значению Void. Если равно, то порождается исключение. Если не равно, то значение x присваивается локальной переменной l и выполняются действия внутри then…end.

Вот, пожалуй, и все основные изменения в самом языке. Еще нужно сказать, что, поскольку язык Eiffel поддерживает обобщенное программирование, то теперь при указании параметров шаблонов (используя С++ную терминологию), можно указывать, является ли параметр attached- или detacheable-конструкцией. Т.е. можно написать LINKED_LIST[MY_TYPE] или аналогичный по смыслу LINKED_LIST[attached MY_TYPE], либо LINKED_LIST[detacheable MY_TYPE].

Отдельно проблема шаблонов проявилась в таком библиотечном классе, как ARRAY[T]. Теперь невозможно создать ARRAY без начального значения для всех элементов, если Т является attached параметром – обязательно нужно будет предоставить не равное Void начальное значение.


Теперь несколько слов о том, почему я решил затронуть эту тему.

Во-первых, потому, что разработчики Eiffel утверждают, что Eiffel стал первым промышленным языком программирования, в котором проблема Void safety решена на уровне языка. И, наверное, они правы. Раньше я слышал об аналогичных решениях в Spec# (на который ссылаются и авторы статьи), а так же видел подобное в языке Nice. Но и Spec#, и Nice являются экспериментальными языками, тогда как на Eiffel уже давно пишут серьезный софт в разных прикладных нишах.

Во-вторых, интересен сам факт того, что люди решились на серьезные изменения в языке, которому больше 20 лет от роду. И при этом не потеряли слишком много в совместимости. Так, авторы статьи приводят статистику: при адаптации стандартной библиотеки EiffelBase было исправлено ~11% кода (9093 строки из 82459), а при адаптации оконной библиотеки EiffelVision – меньше 3% (10909 и 376592). Предположительно, в прикладном коде изменений должно быть еще меньше, т.к. работа с Void значениями более характерна для библиотечного кода.

В-третьих, мне понравилось то, какие цели ставили перед собой разработчики Eiffel-я, работая над решением задачи Void safety. Приведу несколько характерных цитат.

Требования к механизму обеспечения Void safety:

1. Static: полная безопасность обеспечивается во время компиляции.
2. General: применим к обобщенным типам и многопоточности.
3. Simple: нет таинственным правилам; для программистов – легкость освоения; для разработчиков компиляторов – легкость воплощения.
4. Compatible: минимальное расширение языка; следование духу языка; отличное сочетание с другими конструкциями; не ограничивает выразительность для программиста; минимальные изменения для существующего кода.

В пояснениях к этим требованиям мне понравилась фраза:

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

Вполне заслуженный камень в огород C++, имхо. Да и современным объектно-функциональным гибридам так же есть о чем подумать (с намеком на контравариантность в Scala).

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

воскресенье, 4 октября 2009 г.

[comp.prog] Несколько слов о контравариантности в Scala

Года три назад пытался серьезно изучать язык Scala. Такой своеобразный язык оказался – поначалу нравится, потом перестает. Одним из камешков, о которых я чуть не сломал зубы, оказалась контравариантность. Это такая странная лабуда, которую с ходу и на пальцах не объяснишь. А объяснять придется. Вот парочка такие объяснений:

  • совсем свежее объяснение на RSDN от nikov.
  • а вот и две мои попытки почти 2.5 летней давности: общие слова и конкретный пример. Кстати, тот конкретный пример дался мне нелегко и я им до сих пор горжусь, т.к. практически не видел аналогичных простых примеров, демонстрирующих полезность контравариантности.

Про Scala вспомнил потому, что позавчера у себя на компьютере нашел книгу Programming in Scala. Захотелось прочитать, приобщиться, так сказать, к современным веяниям. Думалось: вот разгребусь в ближайшие недели с последним оставшимся безнадежным проектом, возьму отгулов недельку, почитаю спокойно… А тут наткнулся на обсуждение контравариантности в Scala и подумал – а оно мне вообще надо? Имхо, как раз из-за подобных вещей Scala в ближайшее время языком для массового использования не станет. Проще нужно быть, к народу ближе :)