четверг, 14 октября 2010 г.

[prog.flame] Почему я не использую языки D и Go

В комментарии к одной из предыдущих заметок ув.тов.Quaker задал мне непростой вопрос:

Евгений, хотелось бы Вас спросить: а у Вас какое мнение относительно D vs Go? Есть у кого-то из них реальный шанс?

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

Итак, почему же я не использую языки D и Go? Да все очень просто: они еще сырые. Ни один из них не был официально объявлен стабильным.

И если по поводу Go здесь все более-менее ясно, то с языком D ситуация сложнее. У языка D есть две ветки: D1, который официально вышел где-то в 2007-м, и D2, который не совместим с D1, который описан в книге Александреску “The D Programming Language”, но который еще не дошел до официального релиза.

Вроде бы D1 уже давно стабилен, последние изменения самого языка D1 (вроде бы это были текстовые mixin-ы), если мне не отшибает склероз, относились к весне 2007. С тех пор выходят только bugfix-релизы D1.

На тот момент язык D1 был очень даже ничего. Гораздо лучше C++03 и, наверное, даже получше C++0x. Но, библиотеками и инструментами он не был снабжен никак. Стандартная библиотека Phobos там была ущербная, более качественная и удобная Tango находилась в каком-то промежуточном состоянии – новые релизы тогда выходили более-менее регулярно, но не были достаточно стабильными. Других библиотек или D-шных оберток вокруг C-шных или C++ных библиотек было крайне мало (да и сейчас не густо). В общем, начать какой-нибудь proof-of-concept проектик можно было (что я и пытался сделать с помощью студента-практиканта), но было понятно, что до production-применений нужно ждать еще года 1.5-2 (в лучшем случае).

И вот тут Брайт с Александреску прошлись серпом по моим светлым мечтам :( Было заявлено о начале разработки D2 с принципиально другими подходами к обеспечению константности и иммутабельности данных. Поскольку константность и иммутабельность я люблю (наличие константности в C++ сильно компенсирует мне другие недостатки этого языка), то стало понятно, что какой бы код на D1 я не написал сейчас, после выхода D2 меня ждет big rewriting. Что сразу же поставило крест на желании делать что-либо серьезное на D1. А D2 так до сих пор и не вышел.

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

При пропихивании нового продукта в массы различают следующие категории потенциальных покупателей: Innovators (которые любят все новое и любят рисковать), Early Adopters (которые менее рискованные, чем Innovator-ы, но готовы пойти на риск, если видят, что новая технология может дать им преимущество), Early Majority (которые готовы к изменениям, но только если видят, что эти изменения уже себя зарекомендовали), Late Majority (которые пойдут на изменения, если увидят, что большинство вокруг уже сделало этот шаг и не жалеет об этом), Laggards (это самая консервативная часть, которая решается на перемены только когда необходимость перемен становится очевидна всем).

Так вот, сейчас языками D и Go, на мой взгляд, интересуются только Innovator-ы и, может быть, Early Adopter-ы. Себя же я причисляю к Early Majority. Поэтому внимательно на D и Go я буду смотреть только после того, как Early Adopter-ы докажут всему миру, что данные языки вышли из состояния детских игрушек. А для этого нужно, как минимум:

  • наличие стабильных реализаций на большом количестве платформ. Может быть даже разных реализаций от разных производителей;
  • наличие инструментария (как минимум: отладчики, профайлеры) для большого количества платформ;
  • наличие достаточного количества бесплатных и легкодоступных библиотек общего назначения (самых разных: работа с командной строкой, обертки вокруг zlib, поддержка UUID, генераторов случайных чисел, средств работы с файловой системой, с сетью, биндинги к графическим библиотекам или собственные графические библиотеки, криптография, XML и пр.). А так же явно выраженная тенденция к их увеличению;
  • наличие заметных и знаковых success stories, причем, в разных областях.

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

Язык D рассматривается как улучшенный C++. И, наверное, таковым является. Тогда как я слышал мнение, что Go – это улучшенный C. И с этим мнением я согласен.

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

Поэтому на данный момент язык Go меня не интересует.

Язык D интересует чуть больше, но это, скорее, просто чисто исторический интерес. Я считаю, что язык D переусложнен, что есть в нем вещи, которые будут мешать на нем нормально программировать. В частности, когда я пытался его использовать, меня раздражало наличие value и reference типов, но при этом наличие указателей. В D2 появилась транзитивная константность (и только она), что так же не есть хорошо с моей точки зрения. В общем, как язык D немногим лучше C++ – мегагуру будут на нем писать нормально, а простые смертные плодить баги, как и на C++.

Поэтому и к будущему D я отношусь скептически.

А вот будет ли у этих языков будущее – это интересный вопрос. Я думаю так: у Go оно выглядит более радужным, чем у D. Ведь D все эти годы развивался с какой-то непонятной целью. Т.е. авторы хотели создать хороший практический язык, но где та практика, на которой обкатывались их решения? Тогда как у Go, насколько я могу судить, ситуация совсем другая – язык развивается по мере реализации реальных систем внутри Google. И если Google-овцы объявят, что Gо официально считается стабильным, да еще расскажут и покажут, что на нем было сделано, то это будет очень весомая заявка. Хотя бы на то, чтобы стать нишевым языком (вроде Erlang). А вот будет ли Go успешным в других прикладных областях – тут я даже судить сейчас не возьмусь.

65 комментариев:

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

Насчёт того что Go это улучшенный C а не С++ и поэтому менее мощный - это ИМХО чушь, происходящая от непонимания идеологии Go.
Да, там нет классов и исключений. И знаете что? Это классно. Язык сделан так, что эти вещи просто не нужны. В нём офигительнейшим образом сделана ортогональность функций, интерфейсов и структур что приводит к тому что НЕ НУЖНЫ ни классы, ни наследование. В итоге язык получается одновременно проще и МОЩНЕЕ С++, и уж конечно же С. Да, подход к программированию обьектных моделей в Go отличается от подхода и C, и С++ и скорее ближе к Haskel с его type class-ами. Но то что подход другой - никак не говорит о том что Go менее мощный чем C++.

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

@Left:

>Да, там нет классов и исключений. И знаете что? Это классно. Язык сделан так, что эти вещи просто не нужны.

Вот и замечательно. Многим людям не нужны ни классы, ни исключения и они с удовольствием программируют на C. Для них Go будет как раз тем, что им нужно.

А вот мне привычнее с классами, с наследованием (временами с множественным), с исключениями.

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

Поэтому рассматривайте фразу "Go является улучшенными С" не как "C++ мощнее Go", а как "Go является эволюцией C", в то время как "D являтся эволюцией С++".

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

> А вот мне привычнее с классами, с наследованием (временами с множественным), с исключениями.

Ну не знаю.
Многие автолюбители считают Ладу лучшей машиной в мире, а есть такие что предпочитают Москвич ;) Но вряд ли это доказывает что эти машины есть венец эволюции автопрома ;)

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

Про мощность применительно к языку - понятно что измерить её количественно сложно, но вряд ли найдётся желающий спорить к примеру с тем что С++ более мощный язык чем С а Haskell более мощный язык чем Visual Basic. Go - очень простой но при этом очень мощный. И это здОрово. Это язык будущего, честно говоря я не вижу хоть сколько-либо заметной ему альтернативы (в классе "язык на замену С++").

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

>И вот к примеру исключения - это то что наверное можно смело отнести к числу подходов неудачных.

В некоторых областях адекватной замены исключениям не придумано. Например, в вычислениях: a = x + c, где операция сложения может привести к исключению. Не записывать же это выражение как r = add_and_assign(x, c, a); if(0 != r) then...

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

>Классы лично меня тоже коробят своей нерасширяемостью (вернее тем что расширяются они только через наследование).

Во-первых, смотря где. В Ruby не только через наследование. В C# вроде бы есть какие-то extension methods.
Во-вторых, статика (а наследование как раз является единственным средством в статически-типизированных языка) дает высокую производительность + некоторые гарантии совместимости по типам в compile-time. Временами это хорошо.

>Про мощность применительно к языку - понятно что измерить её количественно сложно, но вряд ли найдётся желающий спорить к примеру с тем что С++ более мощный язык чем С а Haskell более мощный язык чем Visual Basic.

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

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

Вот к примеру - в каком ещё языке есть аналог goroutines? (да, я знаю что в Erlang, но сейчас не о нём) Это ж мечта идиёта - гибрид fibers и thread pool, и общаются они между собой через каналы которые суть message passing. Да, такое можно и на С++ сделать - но блин, даже если это очень грамотно оформить в виде библиотеки - пользоваться этим в Go очень просто а в С++ будут постоянно торчать из всех боков шаблоны и/или макросы.

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

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

На самом деле подход "no exceptions" очень даже прост.
Для каждой операции у нас есть два варианта - ошибки которые мы можем обработать в данном месте и ошибки которые мы здесь обработать не можем. Всё.
В первом случае мы тут же пишем код по обработке ошибок, во втором - откатываем всю операцию (в google go это остановка goroutine). Заметьте - я не агитирую против exception-safe coding, я агитирую против разветвлённой иерархии exceptions и уж тем более против джавистских checked exceptions

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

> Ну и при написании софта по принципам fail fast исключения, имхо, гораздо лучше кодов возврата или каких-нибудь прерываний и обработчиков сигналов.

Я считал что fail fast это из разряда agile и всякой такой лабуды ;) Не знал что это может быть как-то с кодом связано.

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

>Вот к примеру - в каком ещё языке есть аналог goroutines?

Насколько я понимаю, все это делается с полпинка в языках с нормальными лямбдами. В Ruby, в частности, в С#, в том же D.

Другой вопрос -- что вы будете делать, если стандартных goroutines по какой-то причине вам не хватит?

В D, например, это будет библиотекой. И можно будет свою библиотеку сделать. А в Go?

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

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

> В C# вроде бы есть какие-то extension methods.

Есть, и это как бы уже неплохо. Но насколько я знаю в C# можно добавить только метод, но не интерфейс.

> Во-вторых, статика (а наследование как раз является единственным средством в статически-типизированных языка) дает высокую производительность + некоторые гарантии совместимости по типам в compile-time. Временами это хорошо.

Вот это и есть непонимание Google Go. Это целиком статически типизированный язык. И в нём все эти фишки работают ничуть не хуже чем в С++ ;)

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

>>Вот к примеру - в каком ещё языке есть аналог goroutines?

> Насколько я понимаю, все это делается с полпинка в языках с нормальными лямбдами. В Ruby, в частности, в С#, в том же D.

Опять непонимание Google Go. Нет. Без поддержки компилятора такие вещи не делаются. Лямбда тут не помогает - максимум что можно добиться в языках с поддержкой лямбд это писАть goroutines в виде массива лямбд (а каждая лямбда - это строка goroutine)

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

>Я считал что fail fast это из разряда agile и всякой такой лабуды ;) Не знал что это может быть как-то с кодом связано.

Это очень тесно связано с кодом. Позволю сослаться на свой же поток сознания:

http://eao197.blogspot.com/2009/09/compprog-fail-fast-restart-quickly.html

Исключения хороши тем, что их нельзя забыть (как это бывает с кодами ошибок), поэтому при их использовании fail fast получается автоматом.

Другое дело, что их довели до маразма в Java :(

Andrey Valyaev комментирует...

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

Как я понял, классы там всетаки есть. Это типы с прикрученными методами. Есть интерфейсы, которые можно расширять... Так что почти те же яйца...

Без классов скучно... :) как они юниттестирование делают?

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

>Вот это и есть непонимание Google Go. Это целиком статически типизированный язык. И в нём все эти фишки работают ничуть не хуже чем в С++ ;)

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

Впрочем, давно не единственным. В Scala, насколько я помню, mixin-ы для трайтов есть.

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

> Исключения хороши тем, что их нельзя забыть (как это бывает с кодами ошибок), поэтому при их использовании fail fast получается автоматом.

То что их нельзя забыть - это свойство не только исключений. В Google Go есть понятие паники (panic) которая по дефолту грохает гоурутину. "Забыть" её так же невозможно как и исключение.

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

> Стоп. Сами же сказали, что в Go нет классов. Поэтому Go не попадает под категорию статически-типизированных языков с классами, в которых наследование было единственным способом расширения.

В Go нет классов.
В Go есть структуры, функции и интерфейсы. И Go полностью статически типизирован. Но каждой структуре можно добавить поддержку интерфейса просто прописав для неё реализацию всех методов которые декларирует этот интерфейс. В отличие от С++ где виртуальные таблицы пришиты к структурам намертво кровельными гвоздями в Go они отвязаны. И это офигительно простая и красивая идея.

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

>Лямбда тут не помогает - максимум что можно добиться в языках с поддержкой лямбд это писАть goroutines в виде массива лямбд (а каждая лямбда - это строка goroutine)

А вот здесь нужно раскрыть ваше утверждение. Поскольку то что я помню про goroutines (вот что), не согласуется с вашим высказыванием. Может власть уже поменялась, а я и не знаю?

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

>То что их нельзя забыть - это свойство не только исключений. В Google Go есть понятие паники (panic) которая по дефолту грохает гоурутину. "Забыть" её так же невозможно как и исключение.

Так и в C-ях такие вещи давно были -- передаешь отрицательное значение в sqrt и получи сигнал, который валит программу. Исключения хороши двумя вещами:
1. Их можно перехватывать и обрабатывать.
2. С ними можно передавать изрядное количество вспомогательной информации.

Не хочешь обрабатывать исключение -- получаешь тот же самый panic.

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

>Но каждой структуре можно добавить поддержку интерфейса просто прописав для неё реализацию всех методов которые декларирует этот интерфейс.

По-моему, для этого дела есть термин какой-то. Вроде структурной эквивалентности.

>В отличие от С++ где виртуальные таблицы пришиты к структурам намертво кровельными гвоздями в Go они отвязаны. И это офигительно простая и красивая идея.

А не из-за этой ли идеи Go принципиально не дружит с DLL-ками? Ведь то, что не удалось связать в compile-time, то уже и не свяжется.

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

@Left: вот, можно посмотреть, что в D2 удалось сделать на лямбдах и шаблонах (раздел 13.6.2 Matching Any Message):

receive(
(long x) { ... },
(string x) { ... },
(double x, double y) { ... },
...
(Variant any) { ... }
);

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

> А вот здесь нужно раскрыть ваше утверждение. Поскольку то что я помню про goroutines (вот что), не согласуется с вашим высказыванием.
По ссылке неправильно понятый (неверно переведённый?) кусок документации из Google Go. Всё не так, и не так оно было с самого начала. goroutine - это не потоки, они ближе к fibers - несколько goroutines могут работать в одном потоке (именно поэтому они легковесны, их можно запускать миллионами).

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

> 1. Их можно перехватывать и обрабатывать.

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

>2. С ними можно передавать изрядное количество вспомогательной информации.

Как правило в этом случае получается "программирование на исключениях".

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

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

goroutines - это НЕ трединг.

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

Вот отличная ссылка про Google Go

http://gaperton.livejournal.com/39529.html

С описанным в посте мнением согласен на 100%, рекомендую к прочтению.

Andrey Valyaev комментирует...

Left> Как правило в этом случае получается "программирование на исключениях"

Эта информация может быть использована для отладки.

А может быть и во зло конечно. :) от этого ни один go не спасет. :)

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

>Всё не так, и не так оно было с самого начала. goroutine - это не потоки, они ближе к fibers - несколько goroutines могут работать в одном потоке (именно поэтому они легковесны, их можно запускать миллионами).

Скорее, вы не правильно прочитали то, что у меня написано. Я сказал, что goroutine будет работать на отдельной нити, и что goroutine-овские нити отображаются на потоки ОС. Я не утверждал, что goroutine является потоком ОС. Поэтому и использовал два разных термина нить и поток (к сожалению, в русском языке нет адекватных переводов для слов fiber, thread и других похожих терминов).

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

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

>panic тоже можно перехватить и обработать.

В C сигналы так же можно перехватить и обработать.

>Но скорее всего - не нужно, ибо panic означает что произошло что-то что не может быть обработано корректно.

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

>Если может - это должно обрабатываться кодом явно, в том месте где это может обработаться.

Я привел пример с простой арифметической ситуацией. Как по вашему программировать математику без исключений? Через коды возврата или прерывания (aka signals)?

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

@Евгений
В общем, как язык D немногим лучше C++ – мегагуру будут на нем писать нормально, а простые смертные плодить баги, как и на C++

По моему существенно лучше, во первых SafeD во вторых pure, в третьих встроенные динамические массивы с проверкой границ.

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

> Как по вашему программировать математику без исключений? Через коды возврата или прерывания (aka signals)?

Отличный вопрос
Лично я не понимаю как использовать исключения в математике :)
Ну вот поймали мы исключение деления на ноль - вот что нам даёт знание того что это именно деление на ноль (другие варианты - корень из -1, или что-нить такого типа)? Мы поймали ошибку после которой не можем восстановиться (потому что в основном control flow программы это не предусмотрено). Значит единственное что можно сделать - прекращать считать ВСЮ операцию и откатываться до последнего вменяемого состояния. Ну и в логи гадить, да :)

Left комментирует...
Этот комментарий был удален автором.
Left комментирует...

goroutines работают в одном потоке. Каждая работает до тех пор пока не словит блокирующую операцию, после чего в ТОМ ЖЕ потоке запускается следующая goroutine (на самом деле поток вовсе не один, их целый пул что позволяет эффективно использовать процессоры с несколькими ядрами).

Проблема в том что если реализовывать то же самое на С++ мы упрёмся в то что для каждой goroutine нам нужен свой стек. В С++ стек не сегментирован (в отличие от Go, да) т.е. нам прийдётся для каждой "C++ coroutine" аллоцировать свой непрерывный кусок адресного пространства - что на 32-разрядных процессорах приведёт к тому что либо получится маленький стек либо получится out of virtual memory при большом количестве "C++ coroutines".

Andrey Valyaev комментирует...

Left: Проблема в том что если реализовывать то же самое на С++ мы упрёмся в то что для каждой goroutine нам нужен свой стек.

Вопрос в том, а нужно ли миллион независимых потоков в C++?

Насчет тяжеловесности горутин - понял, был неправ. Это что-то типа wave (где-то я такой термин слышал) типа легковесные потоки, которые вполне спокойно уживаются в одном потоке... fiber?

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

>Значит единственное что можно сделать - прекращать считать ВСЮ операцию и откатываться до последнего вменяемого состояния. Ну и в логи гадить, да :)

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

Перехватывать же panic-и... Как из пушки по воробьям, имхо.

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

> Вопрос в том, а нужно ли миллион независимых потоков в C++?
Представим ситуацию - мы пишем игрушку в которой есть миллион персонажей, у каждого есть какая-то своя логика (ну т.е. классов персонажей может быть намного меньше, а вот инстансов - миллионы). Как мы это будем делать на C++? Будем всю логику реализовывать в виде конечного автомата (ручками там или напишем DSL для этого) и будем заниматься диспатчингом всего этого добра на N потоков (потому что охота юзать больше одного процессора). Весьма нетривиальная задачка даже для опытного джедая. В Google Go такие штуки пишутся нативно и в пол-пинка.

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

>goroutines работают в одном потоке. Каждая работает до тех пор пока не словит блокирующую операцию, после чего в ТОМ ЖЕ потоке запускается следующая goroutine

А как определяется блокирующая операция? В Erlang понятно, там VM доступ наружу дает и там известно, какой системный вызов блокируется, а какой нет.

Каким образом это решается в насквозь нативном Go с прямым доступам к средствам ОС?

Andrey Valyaev комментирует...

Математика может быть нужна для вполне себе второстепенных операций - переливание волнами бэкграунда например.

Да нехай она обделится на 0, или обизвлекается корней из -1... Это совершенно не повод прекращать работу приложения.

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

> Весьма нетривиальная задачка даже для опытного джедая.

При наличии библиотек без проблем. Некрасиво, конечно, но надежно.

>В Google Go такие штуки пишутся нативно и в пол-пинка.

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

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

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

> Математика может быть нужна для вполне себе второстепенных операций - переливание волнами бэкграунда например.

> Да нехай она обделится на 0, или обизвлекается корней из -1... Это совершенно не повод прекращать работу приложения.

Оформи как отдельную горутину, всего-то делов.

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

>Оформи как отдельную горутину, всего-то делов.

Которая запустится асинхронно. И поэтому нужно будет синхронизировать получение результата. И не только результата, но еще и кода успешности + какое-то описание причины ошибки.

Не так все просто ;)

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

> А как определяется блокирующая операция?

> Каким образом это решается в насквозь нативном Go с прямым доступам к средствам ОС?

Скажу честно - не знаю. Скорее всего для нативных библиотек пишется ручками. Наверное именно поэтому сопряжение с нативным кодом в Go куда более сложное чем в том же С.

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

> Которая запустится асинхронно. И поэтому нужно будет синхронизировать получение результата. И не только результата, но еще и кода успешности + какое-то описание причины ошибки.

Дело в том что на гоувских каналах это в разы проще чем на с/c++-ных примитивах синхронизации. А запуск в отдельной горутине к тому же позволит всей этой "неважной" математике считаться на отдельном процессоре.

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

>Скажу честно - не знаю. Скорее всего для нативных библиотек пишется ручками. Наверное именно поэтому сопряжение с нативным кодом в Go куда более сложное чем в том же С.

Я думал, что сопряжение сложнее из-за GC, а не блокирующих операций.

И что-то мне не верится про перевод блокирующих операций в разряд асинхронных с освобождением текущей нити для следующей goroutine. Для нативного языка это не есть гуд.

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

>Дело в том что на гоувских каналах это в разы проще чем на с/c++-ных примитивах синхронизации.

С использованием исключений этого вообще делать не придется.

>А запуск в отдельной горутине к тому же позволит всей этой "неважной" математике считаться на отдельном процессоре.

Чтобы отгрести проблем с не thread-safe кодом? ;)

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

@Left: как в go c типами, параметризованными другим типом и числом? Можно ли там сделать myvector< int >, myarray< 3 > ?

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

исключения нужны даже при наличии АлгТД и возможности возвращать АлгТД из функции

кто поймет, смотрите 2 куска кода -- без исключений ошибку требуется форвардить изнутри наружу цикла, а с иключениями не надо (а вообще надо статью накатать)

LogFile lf; int traffic=0; bool bad=false;

while(lf.next_line() ) {
int* i = lf.traffic_from_line();
if(i){
traffic+=*i;
}else{
bad=true;
break;
}
}

if(bad)
...
}else{
...
}

LogFile lf; int traffic=0;

try{
while( lf.next_line() ){
traffic += lf.traffic_from_line();
}
}catch(ParseError){
...
}else{ ///это не с++, а в стиле питона
...
}

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

вот цитата из ответа gaperton-a, ставящая крест на языке до тех пор, пока не сделают дженерики И НЕ ПЕРЕПИШУТ ЛИБЫ ПОД НИХ (в яве это заняло считай 10 лет):

> Насколько там распространён "downcast" когда при компиляции есть некий абстракный object, котор
99;й приводится к конкретному интерфейсу?

Настолько же, как в ранней яве. Абстрактный Object здесь является пустым интерфейсом, interface{}. Так как генериков-шаблонов _пока_ нет, даункаст будет встречаться часто. Генерики обещали добавить.

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

@имя
Там где есть АлгТД в таком коде не будет циклов, в худшем случае будет вложенная рекурсивная функция исключение заменится обычным возвратом. А так скорее всего будет соответствующая ФВП типа take_while.

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

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

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

Такой стиль хорошо знаком программирующим на эрланге, этот паттерн обработки ошибок называется "supervision tree". И он без проблем повторяется в Go.

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

Понятное дело, что другим разработчикам этот стиль незнаком, ибо он крайне непрактичен при отсутствии "легкой многозадачности".

Насчет отсутствия генериков - это, конечно, неприятно.

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

И это именно редкое сочетание выразительных возможностей и простоты.

Скажем, я однозначно предпочел бы Go Javascript-у. И, вероятно, во многих случаях - С/C++. На Go элементарно приятнее писать.

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

Особенно, если речь идет о серверной разработке, и о сложной и объемной системе.

"Ортогональность" Go, в сочетании с поддержкой многозадачности - это очень сильные стороны.

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

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

Само по себе нарезание того, что можно не нарезать, уже является проблемой. Даже в Erlang-е не нужно нарезать без серьезной надобности - там как раз исключения есть.

>Скажем, я однозначно предпочел бы Go Javascript-у. И, вероятно, во многих случаях - С/C++. На Go элементарно приятнее писать.

Я противопоставлял Go не JavaScript-у (на котором бы вообще не стал программировать по собственной воле), и даже не C++. Если выбирать между Go и D, то я бы предпочел D.

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

>Особенно, если речь идет о серверной разработке, и о сложной и объемной системе.

В сложных системах зачастую нужны сложные структуры данных. В качестве тривиального примера -- boost::multi_index. Насколько я помню Go, создать что-то подобное на нем будет гораздо сложнее, чем на D.

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

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

Там рулит подход let-it-crash, при котором ты вообще не пишешь обработки ошибок, и закладываешься только на успешный случай. А когда оно упадет - как бы ни упало - обрабатывается падение процесса, и все.

Потому, что _именно так_ - проще, а не возня с исключениями. И наличие исключений при таком подходе не первостепенно - от них, в действительности, ни жарко, ни холодно.

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

Сильные стороны Go - это многозадачность и ортогональность системы типов. Все языки с традиционной системой типов в реальном серверном дизайне проиграют - в них проектировать сложнее.

А со структурами данных нет особой сложности ни в одном языке. Это хорошо инкапсулируется, пишется один раз, и проблемой не является.

Насчет Javascript - у него есть свои сильные стороны. Ацки гибок благодаря динамике.

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

Да, подход let-it-crash получается проще исключений благодаря тому, что код обработки ошибок получается полностью изолирован от функционального кода, выполняющего работу. И стратегией обработки ошибок управлять проще.

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

@gaperton:

и как же будет выглядеть фрагмент того кода с исключениями на go? в частности, что будет go-routine?

предполагается, что функция traffic_from_line -- библиотечная; варианты ее использования различны, и она не знает, запаниковать ли после появления одной нераспарсенной строки, 10, или не менее одной в каждом файле -- поэтому она не может быть
единственной go-routine

таким образом, придется форвардить неудачу как в обычном языке без исключений, и паника тут не поможет... или в go есть специальный механизм, позволяющий *однократно* задать *окончательный* обработчик паники через цепочку вызова go-routines, а не форвардить панику?

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

@Gaperton:

> Насчет Javascript - у него есть свои сильные стороны. Ацки гибок благодаря динамике.

Вот интересно узнать юз кейсы этой гибкости.

@Left:

с какими проблемами мы столкнемся при реализации удобных go-routines на с++ ?

(возможно, это тянет на пост, а не на комменты)

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

@gaperton:

>В Эрланге исключения есть, но добавлены поздно,

Судя по документации это было сделано в версии 5.4, т.е. больше 10 лет назад.

>и на практике применяются очень редко.

Достаточно скачать исходники OTP, заглянуть в каталог lib и поискать catch в erl-файлах. Их там порядочно. А уж в подкаталогах test так вообще как собак.

>Там рулит подход let-it-crash, при котором ты вообще не пишешь обработки ошибок, и закладываешься только на успешный случай. А когда оно упадет - как бы ни упало - обрабатывается падение процесса, и все.

На практике все оказывается далеко не так радужно.

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

> Судя по документации это было сделано в версии 5.4, т.е. больше 10 лет назад.

AXD301 был изначально сделан без них. Это больше, чем 10 лет назад.

> Достаточно скачать исходники OTP, заглянуть в каталог lib и поискать catch в erl-файлах. Их там порядочно. А уж в подкаталогах test так вообще как собак.

Всего 3.5 тысячи catch-ей, на все-превсе библиотеки. Учитывая общий размер в 240 мегабайт - это ничего.

> На практике все оказывается далеко не так радужно.

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

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

@gaperton:

>AXD301 был изначально сделан без них. Это больше, чем 10 лет назад.

В Java изначально generic-ов не было. Но жизнь показала, что с ними гораздо лучше, чем без оных.

Появление исключений в Erlang-е доказывает тоже самое.

>Всего 3.5 тысячи catch-ей, на все-превсе библиотеки. Учитывая общий размер в 240 мегабайт - это ничего.

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

>Ну, ты о практике применения данного стиля судить не можешь, извини.

Это взаимно, кстати.

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

>>Всего 3.5 тысячи catch-ей, на все-превсе библиотеки. Учитывая общий размер в 240 мегабайт - это ничего.

> Не нужно мешать штуки с мегабайтами. Поскольку если выбросить оттуда все не Erlang-овое, а следом и комментарии, то получится совсем другая картина.

Нужно, нужно. Потому, что никакой "совсем другой картины" в папке erlang/lib получиться не может, и не могло.

>>Ну, ты о практике применения данного стиля судить не можешь, извини.

> Это взаимно, кстати.

Нет, это, конечно же, это не взаимно. :) Разница между нами начинается с того, что тебе, чтобы что-то посчитать, надо _качать_ эрланг, а у меня он, по странному стечению обстоятельств, уже установлен, и я могу набрать "grep" :).

Впрочем, тебе конечно же виднее, как оно в Эрланге принято, правда? :D

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

>Нужно, нужно. Потому, что никакой "совсем другой картины" в папке erlang/lib получиться не может, и не могло.

Если не делать, то не могло. А если сделать то получится, что общий объем дистрибутива otp_src_R14B_erts-5.8.1.1.tar.bz2 в распакованном виде 214Mb, из которых в lib находится 197Mb. Если из lib выдрать только непустые строки из *.erl файлов и убрать строки-комментарии то их остается всего на 51Mb в количестве 1344633 штук. В которых обнаруживается 7048 слов catch. Т.е. по одному catch на 190 строк.

Может я чего-то не понимаю в программировании, но один catch на 190 строк -- это очень сильно непохоже на "никто не использует".

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

>Разница между нами начинается с того, что тебе, чтобы что-то посчитать, надо _качать_ эрланг, а у меня он, по странному стечению обстоятельств, уже установлен, и я могу набрать "grep" :).

Впрочем, тебе конечно же виднее, как оно в Эрланге принято, правда? :D


О да, знание принципов, которые закладывались в Erlang и которые описывались в диссертации Армстронга, определяются наличием распакованного дистрибутива. Афигительный критерий.

Может проще сразу линейку достать? ;)

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

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

К сожалению отслеживая инфо по го картина печальная. Они заложились на рантайм при дизайне языка и так и не нашли в том же гугле тех бы его сделал. вместо этого они там новый гцц написали походу :)) Так что ни scalability ни safety от гонок так и не получилось. Ждать го++ ? ;) Имхо нет.
best regards.

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

>товарищу АА нужно было найти brighter писателя компиляторов (злой юмор)

+100500 :)))

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

@eao197

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

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

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

Кстати, перед тем, как линейкой размахивать - скажи, про такие конструкции как panic, recover, и defer в Go - слышал? :) На, полюбопытствуй, и после этого еще раз скажи, нужны ли там исключения.

http://blog.golang.org/2010/08/defer-panic-and-recover.html

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

@Gaperton:

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

Я вот, например, не понимаю, почему для знания принципов supervisors tree в Erlang-е нужно быть экспертом по Erlang-у.

>На, полюбопытствуй, и после этого еще раз скажи, нужны ли там исключения.

Конечно нужны. Но здесь и так уже слишком много сообщений понаписано, поэтому лучше я выскажусь в отдельном посте.

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

@Gaperton: вот мое мнение по поводу Go-шных defer, panic и recover: http://eao197.blogspot.com/2010/10/progflame-go-defer-panic-recover.html