29 November 2011

Гуглиный суслик


Вот ты суслика видишь?... Нет?... А ведь он есть!

I.

Осилил на выходных пару обучающих материалов (типа такого) по новому (?) революционному (?) языку программирования Go от Google.


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

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

Следующий момент -- гарантированое закрытие ресурсов а-ля файлы, сокеты или мютексы. RAII, понятное дело, в языке с GC быть не может. Есть решение из .NET -- using блоки. Похожее решение используется и в Python. Но тут ребята тоже соригинальничали и придумали отложенный вызов функции, т.е. пользователь может задекларировать вещи, которые он хочет сделать при выходе из блока или функции. Непривычно, но с виду вроде как съедобно. 

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

Следующий момент велосипедической болезни, который искреннее удивляет меня -- синтаксис
На сегодняшний день в топ 10 самых популярных языков программирования входит шесть (!) языков использующих (в той или иной мере) си-подобный синтаксис -- Java, C, C++, C#, Objective-C, JavaScript. Во всех этих языках можно придерживаться любого стиля в расставлении скобок операторного блока {}, и это никак не влияет на результаты их интерпретации. Зуб даю, в мире есть миллионы программистов, которые скобки ставят так (и это не является проблемой): 
if (x)
{
   // ... 
}
Создатели Go решили и тут выебнуться и попытались забороть вроде как ненужный символ ";" для разделения операторов. Дойти до человеческого синтаксиса, основанного на форматировании текста (типа как в Python) у них не получилось. От ";" они, в итоге, так и не избавились. Зато теперь в языке скобки {} надо ставить только одним, предопределенным каноническим образом!... Сатиру на подобных мудаков Свифт написал много столетий назад; как видим, увы, до сих пор актуально. 

Что еще?

На мой взгляд, авторы сильно перемудрил решая проблему троицы "ссылки, указатели, значения". Придумывать в XXI веке язык со сборкой мусора, в котором есть указатели -- это за гранью добра и зла. Путь Java, а еще лучше C#, по-моему, закрыл этот вопрос раз и навсегда. 

Но все-таки самой главной проблемой языка я считаю отсутствие generics или templates. Для статически типизированного языка, который пришел типа заменить C++ и порвать всякие скриптовые поделки типа PHP или Python, это абсолютно непростительный FAIL. Даже недобитая Java научилась это делать, а тут, блин, нам такое суют под нос! Ебаны стыд!

II.

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

Мое мнение по поводу этой истории такое. Если мы говорим о необходимости нагрузить все ядра процессора какой-либо задачей, руками разбив ее на N подзадач, то тут, по большому счету, насрать, будут ли это goroutines, или старый добрый код, на основе threads. Потому что реальная проблема лежит не в постановке задач на потоки (это тривиально), а в возможности автоматического распараллеливания линейно написанного алгоритма. И в этом направлении если и теплится какая-либо надежда, то она связана совсем с другой областью -- с функциональными языками программирования.

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

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

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

И тут на сцену выходят сопрограммы. Это такая волшебная штука, берущая лучшее из двух миров, которая позволяет писать код линейно, словно в выделенном потоке, но самого отдельного потока при этом не требовать! 

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

III.

Вернемся к нашим баранам. Вернее сусликам. Есть ли перспективы у нового языка Google, с кем он будет бодаться на рынке и как успешно? 

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


Там, где сегодня работают на C++, в сторону новых языков и не думают смотреть, тем более на такие маргинальные, которые завтра могут благополучно издохнуть, как дохнут у Google их бесчисленные проекты и сервисы. Все, что могли у плюсов отвоевать в области enterprise с помощью Java и .NET, уже давно отвоевали, и, судя по некоторым трендам, отвоевали даже много лишнего -- ибо начался обратный процесс... 

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

.NET -- это о программировании для Windows, поэтому этим ребятам без доброй воли Microsoft прыгать особо не куда. 

Ну и остаются всякие PHP, Ruby и Python'ы. 
Да, Go вроде как должен быть побыстрее, но так ли важна скорость в тех задачах, где сегодня используют эти языки? Вот именно -- на скорость там всегда плевали, или вы собираетесь на полном серьезно подискутировать на тему компилятора из PHP в C++, которые придумали парни из вконтакте фейсбука?
Go, в отличии от этих языков, статически типизирован, но давайте подумаем, а обрадует ли PHP разработчика этот факт?... Вот именно. 
И главное, писать сопрограммы на том же Ruby или Python можно ничуть не хуже, чем на Go. Было бы желание.
И вообще, есть Erlang. Главное, чтобы его хрупкое тельце всякие node.js случайно не затоптали.  

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

Лично я, при прочих равных, предпочту тот же Python.

No comments:

Post a Comment