Pull to refresh

Comments 304

UFO landed and left these words here
Я не переводчик, добавлю от себя.

Попробовал писать на Go несколько домашних поделок, на работе пишу на C#. Если вкратце, я пока так и не смог оценить преимуществ Go, кроме кросплатформенности:

  1. Нет удобных средств для разработчика, IDE со встроенным дебаггером под винду так и не нашёл. Пользуюсь плагином для Idea
  2. Каналы — удобно, но мне они нужны для параллельной обработки массивов данных. В C# это решается проще, Parallel.ForEach и вперёд
  3. Очень непривычно создавать свои ненужные структуры для простой сортировки в Go. И в целом, LINQ для работы с коллекциями в разы удобнее. При работе с Go сильно не хватает
  4. Зачастую, Go кажется слишком низкоуровневым, даже чрезмерно. В идеале, не хотел бы видеть в языке указатели без особой на то причины
  5. Использовал библиотеку написанную на C++ в Go. Не скажу, что очень удобно, а, например, valgrind и вовсе не работает с такими приложениями. Встроенный отладчик память, выделенную в C++ коде, разумеется, не видит. И как отлаживать утечки?


В целом, язык интересный, но для себя достоинств я пока не увидел.
Аналогично, в основном на работе пишу на C#, тогда как хобби C++\Qt\QML. И те же самые замечания! К LINQ очень быстро привыкаешь, к генерикам, к честным ссылкам, к нормальной работе со строками в конце концов (А то в Go это то ли функции из «bytes», то ли «strings», и непонятно, выстрелит ли это в плане локализации)! Хотя некоторые вещи вроде «go» и «defer» хороши.
 From(students).
 Where(func (s T) (bool, error){
            return s.(*Student).age >= 20, nil
    }).


Я правильно понимаю, что (а) внутри Where используется делегат и (б) в этом делегате входной параметр приводится к нужному типу?
Import your []*Student.
Find those over 20. Note T is an alias for interface{}. So we make type assertion to our input type.

Да.
Все равно же делегаты, да?
Нет — в gen используются конкретные типы, поэтому приводить ничего не нужно.
Я не спрашивал, есть ли приведение типов (я вижу, что нет), я говорю, что аргумент Where — делегат. Это ведь так?
А — является ли аргумент Where функтором(лямбдой, замыканием, функциональным обьектом — как хотите) — да является. Честно говоря сложно представить реализацию без функтора.
Либо я чего-то не понимаю в C#, либо в
fruits.AsQueryable().Where(fruit => fruit.Length < 6);


fruit => fruit.Length < 6


Как раз та самая лямбда-функтор-предикат.
Зависит от того, что именно вы понимаете под лямбдой/функтором.

fruits.AsEnumerable().Where(fruit => fruit.Length < 6);


Внутри Where — делегат (анонимная функция), которая будет скомпилирована в исполняемый код вместе со всем остальным кодом вокруг, и выполнена .net-рантаймом в момент итерации.

fruits.AsQueryable().Where(fruit => fruit.Length < 6);


Внутри Whereвыражение (AST), которое в момент компиляции будет только построено, а вот в рантайме будет проанализировано и выполнено тем способом, который нужен провайдеру, лежащему под fruits. Например, преобразовано в предикат WHERE Length < 6, если провайдер поверх БД. Или в $filter=Length lt 6, если провайдер поверх OData.
Черная магия .Net (:

Не знал про такое — но это свидетельствует скорее о умном компиляторе IL кода, нежели выразительности самого языка. Такие вот «макросы времени исполнения», причем подобная «магия» в коде далеко не всегда оправдана. Честно говоря после войн с Hibernate в яве, я очень осторожно отношусь к «умным выражениям». Да и AOT накладывает ограничения на такие вещи.

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

П.С. На C# и .Net действительно очень много вкусных и интересных вещей. Но есть и проблемы — эталонная реализация работает только под Windows, поэтому если у вас Linux\*BSD то продвинуть использование шарпа нетривиально. Вторая этот тот факт, что даже если вам удалось продвинуть Mono — ваша реализация будет медленнее работать и может быть подвержена ошибка среды исполнения (тут был цикл статей о нем). Ну и наконец третье, что экосистема шарпа скуднее по сравнению с той же явой.
Во вторых, Go и C# уж совсем разные сегменты рынка занимают. Откуда такой интерес?
UFO landed and left these words here
Как раз здесь уже не провайдер, а полноценная решение на метапрограммировании шаблонами. Т.е. в данном случае не конечная реализация подстраивается под код, а работает условная «генерация» реализации из требований.
UFO landed and left these words here
В выразительности плюсов никто никогда не сомневался. Вопрос в отладке таких шаблонов (концепты привет) и времени компиляции этого дела.

Из этого кода Boost Spirit вспомнился.
UFO landed and left these words here
Не знал про такое — но это свидетельствует скорее о умном компиляторе IL кода, нежели выразительности самого языка.

Не, IL-компилятор здесь вообще ни при чем. Это чисто C#-ный трюк, когда стрелочная запись в зависимости от ожидаемого типа разворачивается либо в анонимный метод, либо в конструктор AST (очень грубо — в LambdaExpression(CompareExpresssion(MemberAccessExpression(ParameterExpression("fruit"),"Length"), LessThan, ConstExpression(6)))). А дальше это AST уже разворачивается исполняемым кодом.

Это не макрос, это совсем другого рода развлечение, очень мощное, по-своему.
Не, ну это как раз-таки та вещь, которую тут почти единогласно осуждают — использовать привычные для одного языка подходы в другом, где они совсем «неродные».
Вот смотрите, вы же в C# понимаете отличия struct от object? Где они располагаются, как передаются в качестве параметра? Также, думаю, вам вполне понятны значения модификаторов ref и out? А давайте добавим сюда еще unmanaged код, где можно проводить операции с указателями, stackallock и fixed. Получается что в C# тоже не все просто. Кому-то тоже это может не нравится, а кто-то настолько привык, что даже не замечает. Видимо дело в привычке.
Каналы — удобно, но мне они нужны для параллельной обработки массивов данных. В C# это решается проще, Parallel.ForEach и вперёд

Каналы вместе с горутинами по-идее призваны избавиться от callback-hell, который присутствует в некоторых языках.
В C# от него защищают async/await, если я все правильно понимаю.
На C# не пишу, почитал сейчас немного про acyns/await. Насколько успел разобраться, это все же разные подходы.
Го меня цепляет тем, что можно просто писать plain-код. Тот же async/await будет работать не с любой функцией, она должна это поддерживать, а в го можно любое действие дернуть через go… и оно не будет блокировать остальные потоки выполнения. Т.е., тут не асинхронная модель.
В совокупности с каналами и особенно с такой фичей как «select» это позволяет очень легко писать сложную логику управления кучей параллельных задач. Но за это мы платим тем, что нужно следить за потокобезопасностью кода, в го у нас нет гарантий, что в середине функции планировщик не переключится на выполнение другой функции.
Есть.
«Планировщик, я закончил на пока, дай другим поработать в моей треде» горутина говорит либо явно (есть спец функция), либо делая блокирующую операцию (чтение из канала). Примечательно, что если совершается системный вызов, то горутина зависает вместе со всей тредой и планировщик вынужден либо ждать, либо родить еще одну треду. Такие пироги.
Очевидно, что рядом работают в других тредах другие горутины и они могут начать писать туда, куда пишем мы. Но единственный язык, где об этом не нужно думать, это Rust.
> единственный язык, где об этом не нужно думать, это Rust.
Ну почему же. Есть Erlang, Haskell, Clojure, где об этом точно не надо думать.

А рантайм Erlang-а, например, достаточно умен, чтобы выполнять блокирующие системные вызовы на отдельном пуле тредов, что делает работу системы еще более предсказуемой.
Дак вот как раз если горутина слишком долго тупит (это может быть и не системный вызов), то создается еще тред, в который запихиваются другие горутины. Вот тогда мы и получаем возможные проблемы с одновременной записью в map, например, если не учтем этого.
Вот довольно тупой пример, иллюстрирующий ситуацию. pastebin.com/jkH1Nvp2
$ go run check.go
Count 10000; 25.437204715 - 25.484347422
Count 20000; 25.437177393 - 25.605745478
End

Как видим, горутины тут пересекаются по времени выполнения, хотя никаких блокирующих операций не делается.
В Go 1.5 GOMAXPROCS по дефолту ставится равным числу ядер, соответственно, горутины выполняются на нескольких тредах по умолчанию.
То есть, ваш пример не демонстрирует ничего, кроме стандартного поведения.
Я и хотел показать стандартное поведение, что у нас нет гарантии того, что в середину одной горутины не может врезаться другая, например в середине записи в map попытаться тоже получить доступ к этому мапу. Если мы об этом не позаботились.
А, ну ок, я не тот мессидж прочитал.

BTW, в корректности вот этого тезиса

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

я ощутимо сомневаюсь, но не настолько ориентируюсь в планировщике Go, чтобы уверенно отрицать.
Я писал о том, что горутину никто не остановит. В вашем примере ее так же никто не остановил. :) Вы просто запустили две горутины. То, что рядом может выполняться еще горутина и одновременно с нами использовать наши же ресурсы, мне казалось очевидным, о чем я и написал:
> Очевидно, что рядом работают в других тредах другие горутины и они могут начать писать туда, куда пишем мы.

> Вот тогда мы и получаем возможные проблемы с одновременной записью в map, например, если не учтем этого.
Я просто не представляю, как можно не учесть этого. Невнимательность? Да, но а как же иначе? Если язык будет это за нас учитывать (не методами разделения ресурсов аля Rust, а блокировками) то мы, возможно, получим потери в скорости, где нам бы этого не хотелось.
Да, я некорректно выразился насчет остановки. Но технически такое может произойти если два треда попадут на одно ядро. Их будет переключать планировщик ОС.
Я считаю, что синтаксис и текущая реализация языка программирования — вторичны по отношению к экосистеме и сообществу. Где был javascript? А потом пришли ребята с V8, другие ребята с node.js — и все поменялось в течении пары лет. В дизайне самого языка есть спорные момент: GOROOT, отказ от классов, ручное управление контейнерами, контроль ошибок через возвращаемые значения — но все это сделано с определенными целями — серверная разработка «чтобы не текло и не падало». У меня нет большого практического опыта работы с Go. Что видел — работает быстро, но программисты жалуются что много ручной работы на низком уровне. Время покажет куда он будет развиваться и какое вокруг него сформируется сообщество.
UFO landed and left these words here
Тут, по-моему, не угадаешь. Как показывает практика, небольшая группа энтузиастов может угорать по чему угодно, сообщество формируются слабо прогнозируемым способом.
а чего поменялось то? Как была лапша с коллбеками и промисами, так она там же и осталась
В том-то и магия. Ничего по сути не поменялось — а популярность растет. И разработчики думают над способами борьбы с лапшой. Генераторы и async await вот сделали :)
На самом деле, тут все достаточно просто объяснимо, по крайней мере, постфактум.
1. Случайно образовался JS в браузере. Пусть достаточно случайное явление, но язык не повернется назвать магическим.
2. Жесткая конкуренция за растущий пирог случайно привела к тому, что он оказался во всех современных браузерах. Вот это магия, на мой взгляд. Условия рынка сложились достаточно специфические, здесь было вариантов пока еще много. Дальше все строго предсказуемо.
3. Пирог веба вырос настолько, что браузеры стали использоваться почти всеми цивилизованными людьми чуть ли не каждый день. Оно и понятно: почти мгновенная коммуникация — лучшее изобретение человечества пока что.

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

Итак, JS везде и не содержит изъянов, делающих его использование невозможным. Чего ждать? Того, что мы сейчас имеем. Пока веб-платформа не упрется в свои фатальные недостатки, она будет расти в сторону еще большего распространения, улучшения скорости работы и развития инструментов разработки. В принципе, после анонса WASM у меня не хватает фантазии, что еще может выйти определяющего.
Разве что расширение набора инструкций ARM для более эффективной работы виртуальной машины JS, но сначала надо стандартизировать их как следует.

Так что, думаю, популярность растет просто потому что до сих пор не уперлась в потолок, но не просто так, а потому что JS-интерпретатор — самый распространенный на планете интерпретатор/компилятор.
Для javascript особо альтернативы не было по-моему. И, увы, никто так и не создал адекватной альтернативы (а еще — лучше замены связки HTML+JS+CSS) и теперь приходится с этим жить.
Хороший перевод. Но я еще ожидал немного Вашего мнения по теме, ибо его несоответствие с мнением автора особенно выделено перед переводом.
Это дисклеймер, чтобы фанаты Go не слили переводчику карму.
В целом да :). На Хабре не все читатели замечают что какая-то статья является переводом и начинает обращаться к переводчику как к автору. А у переводчика нет года практического опыта работы с Go
Еще одна мелочь в Go, которая меня дико раздражает, — это отсутствие optional arguments. Ну ничего, подумал я, можно ведь выкрутиться через дефолтные значения. Но и они, кто бы мог подумать, не поддерживаются, приходится строить подпорки.

Вы, наверное, догадываетесь, что их там нет намеренно — на практике, по мнению авторов Go, удобство от наличия одноименных функций с разными сигнатурами параметров оказывается сбивающем с толку и запутывающим программистов. Этот вопрос есть в FAQ: golang.org/doc/faq#overloading
Я умею гуглить, спасибо.

Авторы могут считать как угодно. Я не собираюсь говорить им как они должны делать свой язык. просто меня раздражает отсутствие возможности сделать что-то в стиле
func f(n int := 0) {
    // pass
}

вместо вот таких не сбивающих с толку программистов решений.

Особенно после распаковки аргументов в Python (*args, **kwargs), с которым Go часто сравнивают.
Вы совершаете ту же ошибку, что и автор статьи — вы боретесь с языком. Вместо того, чтобы понять, как с его помощью решать проблему, вы пытаетесь понять, как с его помощью сделать так, как я привык в других языках.
Не нужно делать из variadic parameters попытку симулировать argument overloading. Поймите, что решить задачу (реальную задачу, которую должна решать программа) можно сделать несколько иным способом, кроме как «найти любой ценой способ передавать дефолтные значения». И ценой вашего болезненного раздражительного «отвыкания» будет более ясный и читабельный для других код.
Optional arguments можно реализовать через структуру с инициализацией с именованными полями.
Пишу на питоне когда нет особых требований к скорости исполнения, или на D — когда они есть. Счастлив в обоих случаях. Попытка освоить Go окончилась неудачей по причине отсутствия удовольствния при написании кода.
Пишу на го пол года, до этого где то год назад всё в нем не нравилось (первый подход )), но появился проект в котором нужно было написать сложную бизнес логику, работающую по крайней мере не медленно, и всё это за 4 месяца. На java писать не хотел, на Си не умел, нужен был язык который изучишь быстро, в итоге выбор пал на го, и я не разочарован. Как оказалось на го писать легко и приятно, есть возможности профилирования памяти и процессора, быстрая компиляция, стандартная библиотека которая охватывает почти всё. Среду разработки использую LiteIDE, выглядит страшновато, зато работает быстро и всё что нужно есть. Писать тесты на нем легко и приятно. Ну и то что он активно развивается и имеет отзывчивое сообщество это также на мой взгляд огромный плюс. Пока я работал над проектом они выпустили новую версию GC которая уменьшила задержки до неприлично малых значений. Проект в итогде в продакшене, будем смотреть :)
Я меня практический опыт работы с GO всего пару месяцев. Пока вообще далек от проблем в статье, и не факт, что когда-то они передо мной встанут. Сейчас пишу веб-приложение для одного сайта и в целом очень доволен.
Отмечу плюсы:
— язык очень простой
— довольно строгие правила синтаксиса, я понимаю все чужие исходники и думаю другие легко будут понимать, что написал я
— доступны исходники стандартных библиотек
— компилируемость, отпадает потребность хранить исходники на сервере
Из минусов:
— не хватает оператора ( условие? выражение: выражение )
— не такие большие сообщество и база знаний. Не всегда удается найти готовые решения или ответы.
перевод статьи опытного разработчика о его опыте практического применения Go.

Итак, прошел год с тех пор, как я начал использовать Go.


Н-да… о-о-о-огромный опыт… — 1 год попробовал Go.

Статья — бред!
… обиженной девки, надувшей губки: «я такая красивая, а он меня не любит...»
А аргументы кроме ad hominem у вас есть?
аргументы в том, что 1 год — это не тот опыт чтобы рот раскрывать?
Во-первых, учитывая, что Go всего три года — это вполне ощутимый срок.
Во-вторых, за год можно получить достаточное количество как положительных, так и отрицательных впечатлений о языке и инструментарии, которые и изложить в статье.

Но это все не важно, потому что аргумент «один год — не тот опыт, чтобы» — это и есть аргумент ad hominem. В посте изложены вполне конкретные вещи; спорьте с ними, а не с личностью автора.
Go всего три года — это вполне ощутимый срок.

не 3, а 6
Go 1 вышел в марте 2012. Но даже если шесть — это все равно не меняет остальной аргументации.
Вань! Ты посмотри какие клоуны — рот хоть завязочки пришей!


Н-да… сильно я разрушил единомыслие и согласие в таком дружном семействе…

Ты глянь, как по минусам тыцкают! ;-)

По вашему невозможно быть опытным разработчиком без опыта использования Go? И сколько лет должен проработать c Go человек, писавший скажем всю жизнь на Си, чтобы иметь право высказывать свое мнение?
По вашему невозможно быть опытным разработчиком без опыта использования Go?


… в WEB-программировании? ;-)
… или в этом… в DevOps-се? ;-) ;-)

Обсуждать вкус устриц хотелось бы с теми, кто их пробовал.

(с) М.Жванецкий
или в этом… в DevOps-се? ;-) ;-)
Вот есть такие ребята. У них всё на Go написано, номады всякие с консулами. Логично, что люди, которые крутятся в этой кухне, пытаются пробовать Go, потому что авторы Go его для этого самого и позиционируют. Системный язык из Go не получился, остался пресловутый web и DevOps. Кто по вашему должен составлять мнение о Go в таком случае, бородатые системные программисты?
Сама фраза «системный язык» имеет различные смыслы. Вот в этом видео Александреску, Матсакиса, Страуструпа и Пайка спрашивают как раз один из вопросов про то, что вы считаете «системным языком».

Поэтому, если уж дискутировать о позиционировании, давайте озвучим факт, что под этим термином разные люди понимают разные вещи. Кому-то кажется, что они должны писать собственные аллокаторы, а кому-то важно писать продуктивные программы для облаков.
Кстати в Go реально написать свой аллокатор, с помощью syscall.Mmap и unsafe.Pointer например.
Под «системным языком» можно понимать что угодно, но есть чёткие определения, что такое «прикладное ПО» и «системное ПО». На Go пишут прикладное ПО.

Я к Go отношусь совершенно нейтрально, язык свою нишу нашёл, и его используют. Я просто не понимаю, что хотел сказать товарищ выше и что он понимает под вкусом устриц. Но ему, наверное, лучше знать с высоты его 40 лет опыта в программировании. :)
Под «системным языком» можно понимать что угодно, но есть чёткие определения, что такое «прикладное ПО» и «системное ПО».


Нука, нука… Определение — в студию.
Желательно со ссылкой на авторитетный источник.
Я понимаю, что за 40 лет можно всё забыть, в том числе всякие скучные определения, но как искать в гугле, я думаю, вы помните. Выжпрограммист.
Вы не правы. Вам выше привели ссылку на то, где авторы 4х системных языков объясняют, что определения размыты и подразумевают разные вещи.
Зачем писать «четкие определения», если они заведомо не четкие, и их нет, и даже ссылки на субъективные определения, справедливые для какой-то одной эпохи, вы не можете нагуглить?
Хорошо, если вам не нравится слово «чёткие», пусть они будут просто «определениями». Вы о чем спорите вообще, что хотите сказать? В любую, так называемую эпоху были программы, которые обеспечивали работу других программ.

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

(если что, минус не мой :)
Я не защитник гоу, но все таки считаю что ваш аргумент про системный язык требует аргументации.
Я же написал, что «системный язык не получился». Эту мысль можно проследить из этого интервью: sourcegraph.com/blog/live/gophercon2015/123645585015
Q: You mentioned initially having the clear target of being a systems language was important. Is that still the target or has the target changed?

A: At this point, we really think of Go as a general purpose language. That’s also how people use it. It’s being used across the spectrum, and so that’s how we feel at this point.

Q: Most users of Go seem to be server-side. Do you think one way to make Go more mainstream is to attract more client-side developers.

A: The original design was not a standard general purpose language; it was a systems server-side programming language. There’s no ui package, for instance. Now that it’s general purpose, we would really like a ui package, but it’s a lot of work to come up with something that’s cross platform. We could use a lot of experts from the Go community here.
Создатели языка сами позиционируют его как язык общего назначения, на таких языках обычно не пытаются всерьёз писать драйверы устройств, ядра операционных систем или прошивки для микроконтроллеров, но для написания достаточно низкоуровневого server-side кода он вполне подходит.
UFO landed and left these words here
Видимо сей комментатор посчитал, что о языке можно писать лишь после его забвения. Через пять-десять лет мы получим первое поколение опытных АЛГОЛ-программистов и пойдут первые реально хорошие рецензии от опытных разработчиков. К 2030 ждем хороших рецензий на Паскаль, а Java столь молода, что на ней априори пишут лишь юниоры.
Я не считаю GoLang — инструментом для всего подряд, в нем есть полезные вещи как горутины и каналы, поэтому сетевые задачи на нем решать удобно, а в других задачах он уступает другим инструментам.
Что такое «сетевые» задачи? Автор поста пишет, что невозможно изменить параметры создания сокета, не используя syscall — это вообще что хрень такая-то? Нафига тогда го, если есть С?
«Go нужен хорош для сетевого программирования» — мантра которую все повторяют, но видимо уже забыли, что это вообще значит.
Очевидно, имеется ввиду не столько «сетевое», сколько «многозадачное с долгими ожиданиями событий для продолжения задач», когда создавать по потоку на каждую задачу — дорого, а делать всё в один поток — медленно. Спасением тут является кооперативная многозадачность (горутины, гринлеты, волокна, легковесные потоки). Многие языки поддерживают её в той или иной мере: Go, NodeJS, Python, D, Rust, C# да в общем любой современный язык.
Очевидно, имеется ввиду не столько «сетевое», сколько «многозадачное с долгими ожиданиями событий для продолжения задач», когда создавать по потоку на каждую задачу — дорого, а делать всё в один поток — медленно.
Это само собой.

Многие языки поддерживают её в той или иной мере
Да, но не так хорошо: NodeJs, Python, D, Rust, C# — там либо async+await либо yield либо callback-hell. Хотя в Python есть gevent, но результат выходит гораздо медленнее чем на Go. Вот Erlang — конкурент, но он мне не очень нравится, может ещё какой-то язык подскажите? Вот Rust был бы не плох, но там нет микро-тредов либо с ними беда. Поэтому для меня решения на Go в качестве «сетевых тулзов» на первом месте, (для веба и тяжелых расчетов он уступает питону, с++ и другим).
Не вижу принципиальной разницы между await+async, go+chan и yield+run. Во всех упомянутых языках кооперативная многозадачность реализуются примерно одинаково.

Кто бы сомневался, что Python медленнее Go.

Подсказать могу — D на голову лучше Go. Когда наиграетесь в гопесочнице — добро пожаловать во взрослую джизнь :-)

Насчёт легковесных потоков в Rust не знаю — он такой же как Go в том смысле, что дизайн крутится вокруг одной единственной фичи, а остальные не реализуются с отпиской «это сложно в реализации поэтому не нужно» (я про исключения, не говоря уж про лисповые условия).
Не вижу принципиальной разницы между
Разница есть, в await и yield, ф-ии и библиотеки разделяются на 2 типа, при этом есть проблемы вызова одних типов из других. В go только один тип, поэтому такой проблемы нет и не нужно городить «синтаксический мусор».

Подсказать могу — D на голову лучше Go.
В нем тот же yield, а С/С++ он не заменит из-за скорости (а если скорость не нужна тогда можно заюзать хоть питон).

дизайн крутится вокруг одной единственной фичи, а остальные не реализуются с отпиской «это сложно в реализации поэтому не нужно»
Согласен, но это совсем другая история :).
1. Один тип легко преобразуется в другой.
2. Далеко не во всех языка есть эта разница.

1. Там те же волокна, что и в Go и нету костыльных «генераторов».
2. С чего бы ему быть медленней, если он такой же компилируемый, с опциональным сборщиком мусора и кастомизируемыми аллокаторами памяти?

1. Один тип легко преобразуется в другой.
Это не так, попробуйте преобразовать Django в async+await…
Какие-то небольшие библиотеки ещё можно преобразовать, но кто потом их будет поддерживать?, и опять же — раздвоение на 2 типа.

1. Там те же волокна
Вот это уже получше, попробовал — не плохо. Но раздвоение сохраняется, например vibe.d дублирует функционал стандартной библиотеки. Хотя для мелких проектов это не страшно. Надо будет проверить на сколько fiber быстрее/медленнее чем корутины от го.

2. С чего бы ему быть медленней,
Где то на dlang сайте вычитал — они рекомендуют использовать C если нужна бо'льшая скорость, хотя судя по мелким тестам пользователей он почти не отстает от c/c++.

Вообщем, язык интересный, только похоже комьюнити очень маленькое, проблема с докой и примерами, возможно попробую его на какой-нибудь мелкой задаче.
> Это не так, попробуйте преобразовать Django в async+await…

Беглое гугление: http://media.codysoyland.com/pdf/django-on-gevent.pdf

> Но раздвоение сохраняется, например vibe.d дублирует функционал стандартной библиотеки.

А он-то тут при чём?

> Где то на dlang сайте вычитал — они рекомендуют использовать C если нужна бо'льшая скорость

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

> Вообщем, язык интересный, только похоже комьюнити очень маленькое

Ну да, его не пиарят на каждом углу как сабж.
Беглое гугление
Это не то, не микро-треды, я специально написал «async+await», можете не гуглить, за это никто не возьмется.

А он-то тут при чём?
Я его попробовал в качестве асинхрононнго фреймворка работающего с сетью, дак вот он частично дублирует стандартную либу, про что я и говорил.
Если не vibe.d то что использовать для асинхронной работы с сетью?
gevent — это те самые «микротреды», о которых шла речь в этой ветке http://www.gevent.org/
Что именно вы вкладываете в async-await мне не ведомо.

Там есть разные либы, но vibe.d наиболее богатая в этом плане. Это же хорошо, когда у вас есть выбор между несколькими реализациями одного функционала, конкуренция, все дела, разве нет? :-)
Основная проблема с D, которая я вижу — отсутствие продвижения языка каким-нибудь монстром. Из этой проблемы вытекают более мелкие, но не критичные: слабый выбор IDE, органиченный выбор сторонних библиотек и фреймворков,. Однако качество языка и стандартных библиотек продаёт само себя, поэтому надеюсь что в ближайшее время ситуация исправится.
Сделал docker для D lang, кто хочет попробовать, запускать так:
> docker run -it lega911/dlang
# dmd --version
> В go только один тип, поэтому такой проблемы нет и не нужно городить «синтаксический мусор».
Судя по короткому экскурсу в std.concurrency D — тамошние файберы работают как горутины и Fiber.yield возвращает с любого уровня вложенности вызовов => синтаксический мусор не нужен.
Fiber.yield возвращает с любого уровня вложенности вызовов
Кстати в этом есть плюс — мы контролируем когда нам переключится, значит для глобальных/общих объектов в пределах потока локи не нужны в отличие от Го.
И минус — вы должны четко понимать, кто за кем следует, дабы не допустить блокировки или лишнего ожидания корутин. По сути вы выполняете работу планировщика, только вручную. Более того, ваши коллеги должны быть крайне внимательны при работе с таким кодом — наличие незащищенного shared state не является потокобезопасным, но у вас возможна работа с ним из разных потоков управления, что вызывает вопросы. Лично я такое решение считаю не очень удачным, и крайне не устойчивым — есть большая вероятность отстрела ноги, а ловить такие баги потом крайне неприятно и долго.
И минус — вы должны четко понимать, кто за кем следует
Да там полно минусов, поэтому горутины из Го — это значимая фича.
В D состояние по умолчанию не разделяется между потоками. Правда реализация передачи сообщений между ними использует блокировки. Не знаю, почему не реализовали неблокирующую очередь сообщений.
Так в Go тоже же самое, просто эти yield-ы спрятаны внутрь операторов работы с каналами и код работы с сетевым i/o. Я же говорю, файберы из D _по сути_ идентичны горутинам.

Там могут быть различия в реализации, но суть та же.
> D на голову лучше Go
Не, ну давайте все же будем честны — в плане concurrent рантайма и интеграции его с event loop D до Go еще расти и расти, а кто будет за это платить — неясно.
Что же в Go такого удивительного в рантайме и интеграции? Я вижу лишь небольшую разницу в синтаксисе.
Ничего удивительного там нет, однако уже есть годный multicore планировщик с work stealing и уже написан неплохой враппер для асинхронного i/o на горутинах.

Повторюсь, для Go оно уже есть, для D — непонятно когда будет и будет ли. Вся разница в этом.
Давно уже есть: https://gist.github.com/nin-jin/5f6969cb9063b1977769#file-coroutine-d — полный эквивалент горутин. Можно даже синтаксис сделать такой же приятный.
В Go варианте вывод какой-то странный — по две итерации каждой горутиной. В D в один поток они просто чередуются (в примере — два).
Не буду спорить, с D я не могу так навскидку разобраться, как там рантайм устроен.
Честно говоря — тяжело читать, у D нет своего онлайн компилятора?

Второе если я правильно вас понял — в вашем примере они действительно выполняются друг за другом асинхронно, но что не отменяет того факта, что writeln вызовет соответствующие функции ОС — и поток заблокируются — и вместе с ним все корутины ожидающие выполнения в этом потоке. В го печать на экран не блокирует поток горутины.

Я мельком глянул vibe.core.stream — как я понял, там для неблокирующей операции нужно опросить ОС сколько уже готово, вручную.
Внезапно нашёл: http://dpaste.dzfl.pl/

Ну, запустите writeln в отдельном потоке.

Напишите один раз и вынесите в отдельную обобщённую функцию. А если ещё и опубликуете это модулем в репозитории, но получите плюс к карме. :-)
В Go варианте вывод какой-то странный — по две итерации каждой горутиной.
Возможно они в разных потоках, при этом в одной горутине принт срабатывает чуть позже, а после sleep чуть раньше, и выглядит как будь-то они «слипаются».

Разница такая — если одна горутина заблокируется (бесконечный цикл или блокирующее io), то D приложение встанет колом, когда в Го приложение будет работать, Mikanor про это отписался.
Горутины выполняются параллельно, когда это возможно по мнению планировщика. Отсюда некая недетерминированность I/O. В примере на D sleep выполняются асинхронно, но друг за другом, в том время как в го асинхронно, но параллельно.
В примере на D даже глобальные состояния разные. О чём вы?
Судя по цифрам — они либо в одном потоке, которого вполне хватает для таких задач, либо в разных, но разделяют одно состояние, что ещё хуже.

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

Для интереса сделал набросок балансировщика на dlang + vibe.d, тут не весь функционал и он уже отстает от версии на го на ~15-20% (запускал их в одном потоке + 10 клиентов и 10 ворекров). Может там нужно что-то «подтюнить»?
Не очень понял что там происходит. Может целиком бенчмарк выложите, чтобы я его мог у себя погонять?
Ох, как руки дойдут, но обещать не буду. Надо будет упростить скрипты, просто сделать проксирование.
Начал делать скрипт и с dlang беда какая-то — не возможно получить точное время, нашел рецепт, но он дает расхождения в 10 сек и более. Куда копать?
Подозреваю дело в том, что там считается ещё и время вывода в консоль, лови пул-реквест.
С текущей версий тестов получились такие результаты:
Dlang

26572
26607
26678
26678
...

Go

33764
33292
33787
33403
...

Го быстрее на 26%.
Какой компилятор для D использовался?
Значит тест не представляет из себя никакой ценности. Поскольку DMD сейчас концентрируется на поддержке новых фич, а не на производительности генерируемого кода.

lega, повторите тест используя GDC или LDC?
А со скоростью как в этих сетевых задачах?
У меня есть балансировщик для rpc на python + zeromq и аналогичный на golang, дак вот версия на golang в 3-4 раза производительней (что не удивительно). Кроме этого, не прямые конкурентные решения на C/C++ и Erlang работают медленнее (в разы), хотя в первую очередь это связано с набором фич, и производительность языков тут не сравнить.

Вполне возможно на С++ написать ещё более быструю версию, но возится с асинхронностью и процессами, что берет на себя GoLang из коробки, для меня не оправдано.
Нет, но основная логика приложения та же, (не думаю что ручная работа с сокетами и селектами в питоне даст больший профит чем через zmq).
, дак вот версия на golang в 3-4 раза производительней (что не удивительно)

Это очень удивительно, ищите ошибки в архитектуре rpc на python + zeromq, такого не может быть.
такого не может быть.
Не может быть что питон медленнее чем golang? Я думаю это очевидно что питон медленнее. Вот код.
Питон медленнее Го, но не в три-четыре раз, плюс речь идет о сетевом приложении, где основные тормоза это ожидание готовности ввода/вывода. И если у вас похожая архитектура, во всяком случае и там, и там асинхронная обработка сетевых подключение, то 3-4 раза очень и очень сомнительно.

Готов забрать свои слова назад, если представите адекватные тесты.
Питон медленнее Го, но не в три-четыре раз
Да, не в три-четыре, а больше, если сравнивать скорость исполнения питон-байткода.
Например можете запустить у себя эти 2 цикла, у меня го выполняет в 100 раз быстрее. А в одном реальном проекте был расчет математики и я получил ускорение в 17 раз (и ещё больше при C++).
А вот исходники тех самых балансировщиков, py и го.

У Python'а много других достоинств, и Го там рядом не стоит. (Поэтому я на сервер-сайде использую Python, GoLang, C++ совместно для разных задач).
Пустые циклы очень хорошо оптимизируются компиляторами, удивляюсь что только в 100 раз :)
Математика да, операции с плавающими числами в питоне медленные. Математику в питоне или не считают или считают с использованием специализированных пакетов типа numpy, насколько я знаю.

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

Математику в питоне или не считают или считают с использованием специализированных пакетов типа numpy, насколько я знаю.
Да, питон достаточно быстрый для своих задач за счет того что многие модули написаны на C/C++.

Скажите сколько ядер на машине
2, но это не имеет значения для данного теста (Во всех тестах я делал с ограничением в одно ядро и без).
но это не тот случай

Почему, очень похоже именно на этот случай.

Во всех тестах я делал с ограничением в одно ядро и без

А как вы добились того, что бы Go не форкался автоматом по числу ядер?
очень похоже именно на этот случай.
Если бы цикл вырезался, то приложение не задерживалось бы на 0.4 сек (на 4 сек при увеличении цикла в х10 раз). Для уверенности можете какую-нибудь сложную математику протестировать.

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

Как тестите эти два приложения? Есть такая штука github.com/joshmarshall/tornadorpc
было бы интересно сделать аналог на нем и посмотреть.
Погонял у себя примитивный тест lega (но с py версией с «while i < 1000000000»), Python вообще в 184 раза медленнее Go получился…
Сори, не внимательно прочитал сперва :-)
Причем, 184 раза это python2.7. На python3.4 разница вобще в 275 раз.
Можете, ради интереса, попробовать запустить на pypy, — не будет отставать* от golang, если не обгонит.
Я чуть поправил py код:
import time

def main():
    i = 0
    while i < 1000000000:
        i += 1

start = time.time()
main()
finish = time.time()
print(finish-start)


pypy 2.2: 650ms
golang: 405ms

т.е. pypy получился медленнее на ~40%
Да, так уже лучше: у меня разница всего в два раза примерно (6.0 против 3.5).
Добавил в циклы суммирование всех значений i:
go — 6сек
pypy — 165сек

Разница 27 раз. Ну я так не играю)
Возможно из за того, что в этом случае в Го используется long*, когда pypy думает что результат может выйти за 8 байт и переключается на «bigint», а он гораздо медленнее.
Скорее всего. Если не допустить выхода за пределы разница всего в полтора раза.
А я добавил D — он в 2 раза быстрее чем Go отработал.

https://gist.github.com/lega911/c4b627c3f17625f05d44#gistcomment-1595006
dub это уже скомпиленное? Может тогда и Go запускать уже скомпиленное?
dub — это пакетный менеджер. В данном случае, он устанавливает зависимости, компилирует, линкует и запускает.
Причём тут скомпилированное или нет, если замер делается во время исполнения?
Да вроде ожидаемо, на сложных задачах разрыв будет меньше (вон брокер сверху всего в 3-4 раза), а если будут сплошные вызовы расширений (numpy или математика на cext), то питон будет даже быстрее. Рассматривайте питон как медленный менеджер над быстрым С-кодом.
Я сравнивал Go с C++ на несложных алгоритмах на массивах и хешах — по скорости вообще почти нет разницы.
Только в случаях совсем уж сложных вещей, которые хотят всяких -O3 -march и т.п.
Но Go он для сетевых сервисов все таки, а не считать Пи.

«всего в 3-4 раза» больше серверов надо…
Я на одной реальной, математической задаче получил х2 прирост на С, наверно за счет отсутствия GC и более быстрого map.
Но Go он для сетевых сервисов все таки, а не считать Пи.
Вот с этим я согласен, я с этого тред начал.
Один в один мои впечатления от ГО. Правда, год писать я на нём не осилил: ощущение борьбы с языком так и не ушло.
Добавлю так же, что система тэгов для полей структур — тоже очень странная. Выглядит как костылик подставленный опосля («О! А я ещё классную штуку вспомнил»). Соответственно, работа с xml и json заставляет периодически заниматься кодогенерацией, а не динамической генерацией структур.
Каналы и мьютексы МЕДЛЕННЫЕ. Добавление синхронизации через мьютексы на production настолько снизило скорость работы, что лучшим решением стал запуск процесса под daemontools и его перезапуск в случае падения.

Каналы — высокоуровневая реализация модели параллелизма Хоара.
Как высокоуровневая реализация, она и не может быть быстрее или соизмеримой с низкоуровневой моделью семафоров Дейкстра.
Но она свободна от ошибок, в отличие от низкоуровневой модели.

Медленные ли мьютексы Go? Не знаю, не проверял… но какой-то особенной тормознутости в глаза не бросается. Но это низкоуровневый механизм в Go, который — везде там над входом написано, разуй глаза — не рекомендуется использовать.
Но она свободна от ошибок, в отличие от низкоуровневой модели.

От каких ошибок?
От каких ошибок?

От ваших ошибок… от кривых рук ;-)
Ага, конечно. От кривых рук спасают иммутабельность(Haskell, Clojure), share nothing(Erlang) или move semantics(Rust, C++11+).
В Go есть только соглашения и рекомендации, никакой реальной защиты они не предоставляют.
Я не могу понять смысл Go. Если мне нужен системный язык, я использую C/D/Rust. Если мне нужен язык с хорошей поддержкой параллелизма, то я использую Erlang или Haskell.

Параллелизм и конкурентность — очень разные вещи, когда вы переходите к SMP и истинной многопроцессорности.
Параллелизм Erlang или Haskell — кажущийся… логический, если хотите. Эффективное распараллеливание функциональных языков на мультипроцессоры — это больше теоретические изыски, чем практические достижения.
Главная фишка Go — эффективное использование многопроцессорной среды за счёт лёгких goroutine.
Опыт (тестовые результаты) показывает, что они быстрее и эффективнее pthread_t языка C и POSIX.
Не говоря уже про Python и многие другие интерпретируемые языки, где параллельные ветви — это вообще только фикция, модель удобная для формализации и только… а реально они толкутся на одном процессоре только затрудняя выполнение.

Опыт (тестовые результаты) показывает, что они быстрее и эффективнее pthread_t языка C и POSIX.

Сильное утверждение. Покажите тестовые результаты, пожалуйста.
Тесты сравнения горутин и pthread? Ну когда pthread можно будет запустить в одном процессе хотя бы сто тысяч штук, то можно будет сравнивать. А так это все-таки вещи под разные задачи, причем первые «внутри» работают как раз на вторых.
Если goroutine работает поверх posix thread, то по каким критетиям вы сравнивали их скорость и эффективность?
Их сейчас нельзя сравнивать по скорости. Я не понимаю, как Olej смог их сравнить именно в категории «быстрее и эффективнее».
Сравнивать можно разве что в контексте удобства использования и потребления ресурсов каждой из сущностей.
Еще можно сравнить скорость переключения, но ежу понятно, выиграют зеленые треды, где нет трипов в ядро на переключение контекста, они для этого и придуманы.
Спасибо за ссылки. Честно говоря, хотелось бы посмотреть на готовые результаты. Данный тред, безусловно, интересен, но на самостоятельную сборку и тестирование уйдёт слишком много времени.
В readme к platinum searcher есть результаты измерений автором. Простенькие правда, но тем не менее.
вы мягко говоря не в курсе о чём пишете.

Параллелизм у Erlang основан на акторах, т.е. легковесных именованных процессах, которые обмениваются сообщениями через анонимные каналы.

У Go это анонимные корутины, общающиеся через именованные каналы.

Масштабирование эрланга по ядрам — это реальность, причем на много лет более реальная, чем существует Go. Не пишите того, о чём не знаете.
Что вы понимаете под словом «эффективность»? Вы его употребляете в разных контекстах несколько раз.

Параллелизм и конкурентность, действительно, вещи не эквивалентные. Erlang, например, про конкуретность (но, вроде как, Go с ним соперничает в этом). И тут же можно сразу поговорить о том, как работает планировщик в Erlang и в Go. У первого он обеспечивает действительно настоящюю вытесняющую многозадачность, чётко отводя процессам лимит времени (если быть совсем точно, то лимит инструкций) и переключая процессы по достижению этого лимита или раньше (но не позже!). Это обеспечивает невероятную плавность планирования (в купе с механизмами work stealing и настраиваемыми стратегиями распределения работы по планировщикам). Да, ценой некоторой потери в производительности (всё-таки виртуальная машина), но ведь тут речь про регулирование конкуретного доступа к ресурсам, а не числодробление. Я когда год назад искал информацию по тому, как именно работает планировщик в Go, не нашёл ничего, кроме упоминания, что переключение осуществляется на вызовах в runtime. Понятно, что на компилируемом в native-код языке трудно сделать честную вытесняющую многозадачность лёгких потоков, но это так же ставит вопрос, где всё-таки concurrency сделана эффективнее.

Что касается наброса про параллелизм в Haskell, опять же, без определения слова «эффективность» это можно было бы оставить без внимания. Но я просто немного понедоумеваю. Haskell компилируется ровно в тот же самый native-код, что и Go. Треды ОС под планировщиком там ровно такие же. И ровно так же он способен параллельно выполнять код (именно параллельно, до стадии, пока не доделаю, а не пока не попросят освободить ядро). Инструменты для анализа есть. Книга, как делать правильно тоже есть. Где мне этот параллелизм кажется и где отсутствие практических достижений, объясните, пожалуйста?
UFO landed and left these words here
обычно не язык или технология решает, а платформа вокруг которой построен язык, взять тот же самый дельфи и си#
если бы у го был бы хороший mvc фрпймворк или rad платформа все бы сразу побежали бы туда!
это справедливо… но только с точки зрения того, кто ничего кроме Windows не видел ;-)
Так многие и побежали или у вас ест сомнения что гоу вырвался из рамок экзотического языка?
Я либо сражаюсь с ограниченной системой типов с кастами всего в interface{} либо занимаюсь копипастой кода который делает практически одно и то же для разных типов.

Я вот пытаюсь понять, как язык, в котором надо делать такие манипуляции может стать новой Java.
Я вот пытаюсь понять,

Ну и как? ;-)

как язык, в котором надо делать такие манипуляции может стать новой Java.

А почему и как (?!) компилируемый в машинный код язык может стать «новой Java», которая требует интерпретации байт-кода в JVM?
Ну и как? ;-)

И пока не понимаю.
А почему и как (?!) компилируемый в машинный код язык может стать «новой Java», которая требует интерпретации байт-кода в JVM?

Ну Go станет новой Java, если там, где раньше использовали Java, начнут использовать Go. Вы думаете, что тот факт, что Go компилируется в машинный язык, этому как-то помешает?

В том-то и дело, что там не нужно делать такие манипуляции. Если человек все кастит в interface{}, значит он пытается перенести абсолютно чужие и крепко засевшие в голове паттерны туда, где они не нужны. Это печально, таких надо отсеивать на первом же собеседовании :)
Я слышал в Go нет дженериков. Как тогда делать контейнеры наподобие листов? Чтобы при вытаскивании не приходилось кастить элементы к нужному типу.
Дженерики — это не только параметризированные контейнеры, и их важность для реальной разработки программистами из других языков сильно переоценена и приводит к тому, что дженерики используются по поводу и без.

Если вам действительно интересна тема, рекомендую вот этот обзор дискуссий о дженериках в Go: docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/edit#heading=h.vuko0u3txoew
Вот, я же говорил, дженерики нинужны.

BTW, https://gist.github.com/kachayev/21e7fe149bc5ae0bd878 — «Channels Are Not Enough or Why Pipelining Is Not That Easy», отличное чтиво, как раз по теме.
Качаев — знатный кложурщик и Go-хейтер, да )
Кложурщик — если что, это не ругательство :) Я с ним знаком лично, и человек даже на митапе про Go пытался всем навязывать Clojure и рассказывал, что Go и удобства в конкурентности, это не серебрянная пуля, потому что там нет иммутабельных структур, поэтому переходите на Clojure. :)
Ну давайте хотя бы с контейнерами разберёмся. Как в Go сделать параметризированный контейнер?
В том-то и дело, что там не нужно делать такие манипуляции. Если человек все кастит в interface{}, значит он пытается перенести абсолютно чужие и крепко засевшие в голове паттерны туда, где они не нужны. Это печально, таких надо отсеивать на первом же собеседовании :)

Мне показалось, или за полтора часа язык программирования Go действительно радикально изменился?
А вы не отличаете «постоянно сражаюсь с ограниченной системой типов с кастами всего в interface{}» от «как написать параметризованный контейнер»?

Я всё жду, пока вы дойдете до вопроса «А почему авторы Go считают, что в программировании есть что-то иное, кроме параметризованных контейнеров?». Или вы тоже каждый день пишете свои параметризованные контейнеры из года в год?
Ну вроде речь сейчас идёт о параметризированных контейнерах. И, как я понял всё, что туда кладёшь надо кастить в interface{}, а потом обратно. И это следствие ограниченности системы типов.
Ну это вы о контейнерах, а я о шаблонах и паттернах которые человек не хочет или не может пересмотреть по мере того, как он пытается освоить новый язык. Начиная с того, что кастят в interface{} некоторые новички в массе других ситуацией, и заканчивая тем, что параметризация очень часто используется там, где она 100 лет не нужна.

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

Другими словами, если человек привык ездить в Икарусе, и ему неудобно без поручней в Мазерати (не за что держаться стоя), то это не недостаток Мазерати, это недостаток человека, что он так долго адаптируется к новым для себя реалиям.
«War is peace, generics are slavery, interface{} is bliss.»
Ну Оруэлла все читали, давайте что-то по сути.
Давайте по сути.
а я о шаблонах и паттернах которые человек не хочет или не может пересмотреть по мере того, как он пытается освоить новый язык

Какие паттерны предлагает Go взамен тех, которые «не хотят пересмотреть»?

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

Какой майндсет предлагает Go в таком случае?
Давайте.
Я вот на хаскеле пишу. Он ни разу не класс-ориентированный ОО-язык, и набор паттернов в нём тоже совершенно иной. Но параметрический полиморфизм (читайте «генерики») там используется в хвост и гриву, как мощное средство повышения выразительности, производительности и надёжности. Сценарии его использования далеко не ограничиваются контейнерами, полиморфный код можно найти практически в любой библиотеке. И тут вдруг приходят гоферы и говорят, что если достичь просветления, сразу понимаешь, что генерики нинужны; но наглядных примеров, как без них резко всё делается проще, почему-то не показывают. При этом порой забывают, что язык несколько захардкоженных полиморфных типов всё-таки предоставляет (массивы, каналы, что ещё?), только другим их делать не даёт.
Ну так давайте тогда называть вещи своими именами — вы пишете на языке Х, где жить нельзя без дженериков. Где-то вы читаете, что в другом языке дженериков (в привычном для вас понимании) нет, но при этом Google, Dropbox, CloudFlare и куча других компаний пишут высоконагруженный производительный софт.
Вместо того, чтобы задаться вопросом «Ага, видимо в других языках что-то немного иначе, чем в моем любимом Хаскеле, видимо нужно изучить вопрос повнимательнее» вы решаете просто «Язык — отстой, потому что там нет того и в том виде, к чему я привык в Хаскеле».
Ну окей, что тут скажешь.
И что? Высоконагруженный производительный софт и на С пишут – и уж явно поболее, чем на го. Только вот сишники не говорят «всё городите свои классы и интерфейсы, вместо того чтобы пересмотреть свой подход и изучить вопрос повнимательнее», кроме совсем старозакалочных. Кстати, многие там без генериков тоже страдают, и костылят что-то на макросах и void*.
Ну и не надо мне каких-то левых мнений про отстой приписывать. Насчёт «в других языках что-то немного иначе» – параметрический полиморфизм в том или ином виде есть в подавляющем большинстве современных статических языков. Хаскель я привёл лишь в качестве примера, отличного от ООП-мейнстрима. Так что в данном вопросе это не он, это го такой особенный.
Высоконагруженный производительный софт и на С пишут

Да на всем пишут. Вопрос — какой ценой.

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

Класс, что ж народ так страдает тогда на этом «подавляющем большинстве современных статических языков», раз у вас там идиллия?

Все эти «споры ни о чем» не отменяют одного — статья не имеет ни одного выдерживающего критики аргумента. Можно честно признаваться в том, что ты привык к чему-то, или больше душа лежит — но нести откровенную чушь, выставляя это «минусами языка» уж явно не стоит. Ради своей же репутации.
UFO landed and left these words here
Можно честно признаваться в том, что ты привык к чему-то, или больше душа лежит — но нести откровенную чушь, выставляя это «минусами языка» уж явно не стоит.

А в статье, надо отметить, и написано совершенно честно: «поговорим о том, почему я не считаю Go полезным инструментом».

Еще в оригинале есть хорошая фраза, которую переводчик по каким-то причинам выпустил:

If you’re not interested in my opinion, or are ending up here via some Go news aggregator or something and want to show me the error of my ways, you probably needn’t bother.


Человек предлагает свое мнение, и предлагает он его по внешней просьбе.
В русском переводе это звучало бы как пассивная агрессия. Разница в менталитете, вот это все. Нейтральный тон ftw!
Угу, нейтральный тон, а что получилось — видно. Впрочем, было предсказуемо.
Не знаю, в каком порядке вы читали мой комментарий, но страдания были в абзаце про сишку, в которую полиморфизьма тоже не завезли, а не про подавляющее большинство.
Да на всем пишут. Вопрос — какой ценой.

Ну а я о чём. И без генериков пишут, и на го пишут. Только какой ценой? ;)
Ну а я о чём. И без генериков пишут, и на го пишут. Только какой ценой? ;)

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

Мне интересно как живётся без дженериков. Самое простое применение дженериков — параметризированные контейнеры. Интересно как с этим в Go.

А вы в ответ — храните interface{}. Я так понимаю это как в Java хранить Object. То есть ответ такой, что сделать параметризированный контейнер невозможно.

С другой стороны вы утверждаете, что если человек кастит к interface{}, то его надо гнать. Создаётся впечатление, что приведение к interface{} неприемлемо везде, кроме случая с контейнерами.

Я правильно понял?
Если мы так сильно требуем жесткую параметризацию, при этом у нас нет общего интерфейса(в случае го это означает, что у хранимых структур нету ни одного одинакового метода) для хранимых типов — то почему не использовать go generate на готовом шаблоне?
А какие преимущества приносит отказ от дженериков? Чем их отсутствие улучшает жизнь настолько, что вы готовы пойти на использование генераторов кода?
UFO landed and left these words here
Я правильно понял?

Со скрипом, да.

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

Наоборот — я говорю, что злоупотребление пустым интерфейсом в Go есть признак борьбы с языком, а вы все увели в контейнеры.

Мне интересно как живётся без дженериков.

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

Как вам уже написали, авторы Go не против дженериков, но они не знают, как их реализовать, чтобы это ни было также отстойно, как в других языках. Это не политическое решение, а техническое. Дженерики — это всегда компромисс и усложнение языка, и наивность некоторых, что достаточно «больше поработать над компилятором» тут никак не меняет ситуацию.

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

docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/edit#

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

Это неправильная постановка вопроса. Правильная — «как часто вы пользуетесь параметризованным контейнером». Ответ — часто. Я вот — практически ежедневно.
Поглядел документ про generics approaches в go

Pros

standardized complex types

В джаве куча стандартных параметризированных типо, они ведут себя предельно предсказуемо. И наличие дженериков этому не помешало.
smaller language/compiler (if there aren’t many generic types)

И таки да, это правда. Только вы почему-то утверждаете, что дело не в этом.
language constructs can be optimized for these types

Преимущество сомнительное, в С++ при необходимости оптимизации просто определяют специализацию шаблона.
the code is more concrete (because users can build less abstractions)

Преимущество опять же сомнительное, не хочешь абстракций — не делай их.
Прошу прощения, половина поста случайно отправилась.

Cons

each generic type adds complication to compiler

Таки усложняет компилятор, если волшебных generic типов много

each generic type makes the language more complicated

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

the generic types must perform well in lots of cases

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

the language is less flexible (because users can build less abstractions)

Ну понятно в общем, чего коментировать.
Ну а я, давайте, Cons прокомментирую :-)

> each generic type adds complication to compiler

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

> each generic type makes the language more complicated

Ровно в той мере, что появляются плейсхолдеры для конкретных типов — не ахти какая сложность.

> the generic types must perform well in lots of cases

Собственно, не нравится одна обобщённая реализация списка — используй другую. Проблема тут только со «встроенными типами», для которых есть специальный короткий приятный синтаксис. Но встроенные комплексные типы в Go и так обобщённые (массив, канал), хотя возможность поменять и их реализацию была бы классной.

> the language is less flexible (because users can build less abstractions)

Меньше абстракций, как и меньше ключевиков — глупый аргумент. Тем более, что обобщённое программирование наоборот повышает гибкость языка. А вот необходимость копипасты или постоянного приведения типов говорит как раз о костности языка.
vintage poxu — вы ведь понимаете, что вы счас осудили осуждение текущей реализации. Никто не говорит, что текущий подход это идеал — он просто есть, со своими плюсами и минусами, который наглядно выставили.
Mikanor, они не понимают. У них есть мнение о том, с чем они не знакомы, и они тратят колоссальное количество сил и времени, чтобы это мнение насадить и рассказать окружающим.

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

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

Поэтому, извините, господа студенты, но такое общение больше не интересно.
и одна из обязанностей любого программиста — уметь правильно оценивать и выбирать инструменты. Быть не просто кодером, который только может клепать типизированные контейнеры и помнит все виды алгоритмов наизусть, а архитектором, понимающем возможности, недостатки и компромиссы технологий и подходов.

Если бы это было так, то не было бы архитекторов, а были бы сплошные программисты cum architect. Но нет.
UFO landed and left these words here
Мне будет очень приятно, если вы приведёте цитату, в которой я заявил, что я не учил и не хочу учить Go. Да что там, даже цитата, в которой я сказал, что Go мне не нравится, меня порадует.

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

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

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

Ну и конечно когда человек, обвиняющий окружающих в хейтерстве переходит на личности первым — это пять.
vintage да, осуждает.
Что касается меня, то посмотрите внимательнее, что я писал про Cons. Там в основном молчанивое согласие.
А вот Pros, да, сомнительные.

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

В С++ с этим связан SFINAE которой уж точно не ускоряет скорость компиляции.
Вы так и не ответили на вопрос. Как в Go сделать типизированный контейнер, которым можно пользоваться без кастов? Я вот в гугле нашел только или советы писать всё ручками, или делать генераторы кода.
Если человек все кастит в interface{}, значит он пытается перенести абсолютно чужие и крепко засевшие в голове паттерны туда, где они не нужны.

Пожалуйста, каков тогда «правильный» способ это сделать в Go?
Вы так и не ответили на вопрос

Ответил выше.

Пожалуйста, каков тогда «правильный» способ это сделать в Go?

Что «это»? Вы же понимаете, что речь не об отдельных контейнерах, а о массе других случаев, когда народ продолжает мыслить «дженериками» и пытаться их пхать везде где нужно и не нужно. Или не понимаете?
Если человек все кастит в interface{}, значит он пытается перенести абсолютно чужие и крепко засевшие в голове паттерны туда, где они не нужны.

Да что ж там разбираться — храните interface{}, как в golang.org/pkg/container/list.

Таки в каких случаях пихать можно, а в каких – чужие паттерны?
Таки в каких случаях пихать можно, а в каких – чужие паттерны?

Вы ждете ответ на такой вопрос в одном комментарии?
Мне странно объяснять такие простые вещи — сам по себе кастинг интерфейсных типов это ни плохо, ни хорошо. Иногда он нужен, чаще — нет. Любой (со средним соображением), кто не полениться потратить вечер-два, чтобы освоить основу языка, поймет, где нужно «пихать», а где не нужно.
Как следующий код переписать на Go? https://gist.github.com/nin-jin/5f6969cb9063b1977769#file-sorting-d
Ну то есть 25% кода — копипаста. На каждую комбинацию «тип элемента» + «вид сортировки» требуется повторять по 9 строчек однотипного кода. И, кстати, вы потеряли юнит тесты.
На каждую комбинацию «тип элемента» + «вид сортировки» требуется повторять по 9 строчек однотипного кода.

В вашем примере на D то же самое по сути, только более ограничено.
В моём примере копипастой за меня занимается умный компилятор, позволяя мне сконцентрироваться на моей задаче. Вам же приходится водить глупый компилятор за ручку, иначе он без конца падает и отвлекает вас от дела.
Нет, вы все равно определяете функцию Less() для каждой комбинации «тип элемента» + «вид сортировки». Swap() и Len() всегда одинаковы для слайсов(массивов), но в более сложных вариациях тоже может понадобится переопределение и вам тоже придется это делать.

Ну и если вы три строчки экономии в данном конкретном примере готовы променять на все остальные преимущества, которые дает Go, то мне понятно, почему D так и не нашел свою нишу.
Функцию сравнения определяю там, где использую, а не в конце файла. Остальные функции зависят от реализации коллекции, а не от типов элементов. Плюс вы ещё забыли упомянуть, что на каждую комбинацию типа элемента и вида сортировки вы создаёте отдельный именованный тип коллекции, а я не занимаюсь этой ерундой.

«Все остальные» — это какие? Именно преимущества перед D, а не архаичным С, с которым Go обычно сравнивают.
Не понимаю. Конкретная проблема — реализация типизированных контейнеров. Вместо конкретного ответа вы уходите в общие рассуждения о «засевших в головах паттернах». Так как контейнер-то сделать?
UFO landed and left these words here
Конкретная проблема — реализация типизированных контейнеров

В программировании обычно другие проблемы. Какие типы и структуры данных использовать — это уже решение разработчика. Вам же менеджер не приносит ТЗ с текстом «сделай мне типизированный контейнер, и желательно именно так, как ты привык в своем любимом языке».

Так как контейнер-то сделать?

Ответил выше. В третий раз буду игнорировать вопрос.
UFO landed and left these words here
Паттерны — это плохо, особенно, когда человек не способен за их рамки выходить. Есть даже известная фраза, что «Паттерны — это баг-репорт на ваш язык».

То что вы описали — это не «общая идея», а «наиболее отличительная черта». У Go есть три таких черты:
— уход от концепции управления потоками
— convention over configuration — единый формат (go fmt) и тд
— объекты, определяемые поведением (interfaces)

Но единственная задача которую преследует язык — это облегчать ежедневный труд программистов. Он не берет отдельными фишками, он берет всем дизайном языка и тулинга в целом. Это сложно понять, пока не пишешь на Go, соглашусь.
UFO landed and left these words here
Вот в этом, вероятно, и проблема.

Рад, что мы пришли к консенсусу. Собеседники, которые не могут что-то понять, пока не попробуют, а не пробуют, потому что не могут понять, это да, проблема.

Ad-hoc polymorphism что ли? Не ново. При определённой широте взглядов можно сюда хоть duck typing из питона или плюсовых шаблонов, хоть всякие ML'ные семейства систем типов приписать.

Ясно, вы все ищете ответ на вопрос «какая есть новинка в Go, которую невозможно сделать в другом языке». Если для вас на данном этапе развития, как разработчика, это главный фактор для выбора языка, то, Go, конечно же вам не подходит. Go о другом и для другого.
UFO landed and left these words here
Слушайте, ну не видите для себя надобности в языке — не учите. Зачем тратить свое и чужое время, чтобы сообщать, что для ваших задач какой-то язык не подходит?
Есть много студентов и программистов, которым языки интересны сами по себе. Go тут не игрок, Go создан для тех, кому интересней писать реальный продакшн код, быстро и качественно, чем возиться с выразительными языками.
UFO landed and left these words here
Я вам так скажу — то время, которое вы потратили на написание комментариев тут, достаточно, чтобы освоиться с Go и написать какой-нибудь реальный код. Как только для вас процесс написания кода станет важнее процесса «освоения языков», вы поймете всё, о чём я писал выше :)
У меня создаётся впечатление, что после вечера, проведённого в комментариях, я могу рассказать чем хорош Go лучше чем вы. Это при том, что кода на нём я пока что не писал. По моему каждая минута этого времени для меня уже окупилась в тройном размере.
UFO landed and left these words here
Я тут погуглил, почитал и пришёл к выводу, что основная прелесть Go — в моментальной компиляции. Мне кажется она делает прохождение цикла тест-код-рефакторинг быстрым и безболезненным. Всё как советуют гуру TDD.

Ну и не забываем о беспроблемной сборке под все поддерживаемые платформы. Всё, как советуют гуру CI.

И это при том, что сборщик мусора есть и он работает с гарантированной задержкой. Всё как мечтают гуру JAVA.
UFO landed and left these words here
Мой опыт общения с хаскелем, увы, ограничен слепой модификацией pandoc, с целью генерации хабраразметки из маркдауна. Хотя, говорят, вещь хорошая. Можно там пересобрать весь проект и прогнать тесты секунд за 10? Хотя понимаю конечно — зависит от величины проекта. Но хотя бы в субъективно небольшом проекте можно такое? REPL всё-таки не замена TDD.

Вообще в go каждый раз пересобирается всё, что нужно из стандартной библиотеки и все зависимости. То есть решена проблема долгой сборки и тема с необходимостью заранее собирать статистические библиотеки для C++. Ну и jar в java. Если компилятор для платформы есть — программа на Go соберётся без проблем и без разных ключей для разных платформ. Плюс получается один большой бинарник, а не набор файлов.

Сборщик мусора в java может остановить всё на неопределённый период времени и это больно. В Go такой пробемы нет — gc отработает за 10 ms.
UFO landed and left these words here
Насколько я понимаю нет, но я не углублялся в вопрос.
UFO landed and left these words here
Ну тут вы не совсем правы — наложено лишь ограничение — не более 10ms каждые 50ms на GC. Поэтому если не справился за один подход — значит будет пытаться в следующие 50ms. И так далее…
UFO landed and left these words here
Обещают ползунок, положение которого определяет соотношение используемой памяти ко времени сборки.
Этот ползунок был с самого начала, GOGC называется. Он определяет порог роста хипа, после которого нужно запускать GC. GOGC=100 (по умолчанию) означает, что если размер кучи на 100% больше (тоесть, вдвое) количества занятой реальной памяти, то нужно запускать GC.
Вот тут подробнее есть: habrahabr.ru/post/265833 и тут m0sth8.github.io/runtime-1/#1
Ну и еще в подкасте golangshow.com периодически внутренности обсуждаются.
UFO landed and left these words here
Большинство критиков Go пишут, что-то вроде «тут нет фишки, которая нравилась мне в языке X». Я не понимаю. Есть тур. Есть документация по языку. Куча статей best practice и how to use. Нет так нет. И не надо пытаться писать на Go так как писал на языке X.

Для того же бенчмарка количество иттераций настраивается ручками. Для разных задач разное число, зачем мне для простенькой задачки гонять проц 10 минут. Для чего нужен и как работает interface{} вообще мало кто из критиков (и не только) понимает.

Про «сверхвысокую» информативность сообщений об ошибках я согласен. Проще в сорцах на github найти, чем в гугл. GCC со своим C в сравнении просто великолепен (в купе с google и SO).

Для низкого уровня Go подходит плохо. Тому цена кроссплатформенность. Сейчас есть подвижки в эту сторону и пакет syscall более не развивается, вместо него лепят отдельно для каждой системы по пакету вот. Должно быть проще в перспективе. Я думаю что Go лучше сравнивать с Java. Если смотреть в веб — то c Ruby (revel+gorm хоть и отдалёно, но напоминают rails c его activerecord).
Да и кстати append всегда создаёт новый слайс. Но важно помнить, что часть одного слайса может быть так же частью другого. Если что есть copy. Да эта тема довольно проста и хорошо освещена, по крайней мере в англоязычном мире и не нужно хорошо знать язык, чтобы до неё дойти. Чувак просто не разобрался. Вот и всё.

Собственно я том, что не стоит винить язык за свои ожидания в его поведении, если документацию изучить не досуг.
А мне доставляет удовольствие гоу хотя, как и автор статьи, периодически недоумеваю от местных указателей, и строк с []byte.
Да как бы и так ничего нового, но все же… Я ж не программист, а маску на стройке нашёл.
Повеселила статья, которую правильно бы было назвать «Год с моей последней блогозаписи в Go».

Всё что написал автор — попытка более изощренно продать свое «я привык по-другому» за «минусы языка», и я не верю, что он писал год на Go, иначе не голословничал бы от каждого поверхностного суждения, а всё-равно разобрался бы в теме.

Собственно, автор всё сам объяснил вот тут:
Я либо сражаюсь с ограниченной системой типов с кастами всего в interface{} либо занимаюсь копипастой кода который делает практически одно и то же для разных типов.
Такое бывает у программистов, пришедших с других языков, когда они сражаются с языком, пытаясь насадить свои привычные паттерны и подходы к новому языку. Но, как правило, это не длится, даже в самом упоротом случае, больше двух недель. Если же у автора это продолжалось и вправду год, то всё *очень* печально.
Так что, если увидите программиста, который кастит постоянно все в interface{} — особенно на интервью — бегите от него подальше.

Каналы и мьютексы МЕДЛЕННЫЕ. Добавление синхронизации через мьютексы на production настолько снизило скорость работы, что лучшим решением стал запуск процесса под daemontools и его перезапуск в случае падения.
Разумеется, когда каждый может написать за минуту бенчмарк и посмотреть реальную производительность мьютексов и каналов, цифры в посте отсутствуют, как класс. Расчет на то, что читатель все примет на веру и скажет — «да, действительно, раз даже капсом написано, то и вправду медленные».

Строго говоря, утилита для анализа покрытия кода в Go — это хак.

Не важно, что coverage report идет в Go из коробки, не важно, что дает возможность с нулевым усилием получить coverage percentage, не важно, что веб-страничка репорта дает возможность бегать по файлам и смотреть визуально, какие функции и как покрыты. Этот мелкий практический аспект не важен, ведь главное — что это можно назвать «хаком» и, неверно поняв, как работать с инструментом, написать «критику».

Пользователи go хором говоря не пользоваться get, но при этом ничего не делают, чтобы пометить ее как неудачную реализацию и сделать официальную замену.

Все пользуют go get и никто не говорит «не пользоваться get». Это уже становится похоже на типичные приемы пропаганды — говорить заведомо ложные сведения так, как будто нет сомнений в их истинности.

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

Автор абсолютно не понимает зачем нужны бенчмарки функций. Странно, что он не пожаловался на «замедление связанное с падением нод и рестартом серверов».

многие проверки, которые обычно делает компилятор просто пропускаются — они реализованы в go vet.

Бгг. Как же человек старается насадить свое узкое мнение на реальность.

Все, устал комментировать каждую строчку. Автор не читал документацию по Go (иначе бы он понял, как просто работают поинтеры в Go), весь этот год писал на обожаемом им Эрланге, и собрал все, не фильтруя, что он придумал или не понял, чтобы назвать это минусами языка.

Хороший вброс, одобряю.
А можете пояснить начинающему, почему вообще происходит кастинг в interface{}? Зачем он нужен?
В Go несколько уникальный подход — есть структуры и интерфейсы, первые определяют данные, вторые — поведение. При этом любая структура с данными может неявно реализовать какое-то поведение (интерфейс), просто имея нужные методы с нужной сигнатурой.
И тут начинается интересное — если понять эту парадигму, особенно после class-ориентированных «ооп» языков, то многие вещи начинают быть проще и понятней. Становится более ясно, когда мы работает с данными, а когда логикой и можно передавать аргументы функциям как через типы-структуры, так и через типы-интерфейсы. Тоесть один тип может быть и структурой и интерфейсом одновременно.

Но людям, которые привыкли к дженерикам, сложно поменять подход к проблеме, который, как известно, формируется инструментарием. Они пытаются в Go все засовывать в некое подобие дженерного типа (interface{} — пустой интерфейс, которому, по логике вещей, удовлетворяют все остальные типы-структуры), и работать с ними. Это очень печальный признак, потому что на Go так не пишут и так даже думают о проблеме.

Вот одна из статей, объясняющая интерфейсы: go-book.appspot.com/interfaces.html
В Go несколько уникальный подход — есть структуры и интерфейсы, первые определяют данные, вторые — поведение.

Я вот недавно читал книгу Чистый код, Роберта Мартина. Примеры чистого кода приведены на Джаве. И там то же самое, только интерфейсы заменены объектами. А ещё уникальным подходом это никто не называет.
По идее, после этой книги, вы должны легко отличать «тоже можно сделать» от «основа дизайна языка».
Вы правы, но лишь частично — в отличии от Go, в Java одинаковый набор методов у двух интерфейсов не означает типовое равенство этих интерфейсов. Как и в большинстве ОО языков. Таким образом, если другой человек написал класс и интерфейс к нему, то вам придется либо использовать его интерфейс в своем коде выполняющим с его объектами работу. Это удобно, но до тех пор, пока вы вдруг не понимаете, что у вас есть объекты обладающие похожими методами и код можно вынести в один. Дальше вариантов два — либо вы говорите, что ваши классы реализуют еще и этот «чужой» интерфейс, попутно реализуя заглушками возможные дополнительные методы (что скажем так, не красиво) — либо окольными путями приводите чужой код уже к вашему интерфейсу — например через делегирование.

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

П.С. Многие говорят, что это слишком много boilerplate кода для сортировки тех же массивов — специально для них есть пакет с небольшой толикой черной магии, но требующий только функтор-компаратор.
Спасибо за пояснение.

Если я правильно понял, то вы говорите об утиной типизации. Вещь хорошая, но уникальной её не назовёшь. Хотя если говорить о языках со статистической типизацией, то я больше такого не припомню. В С++ вроде что-то такое собираются ввести.
Больше похоже на упрощенные (раз там отсутствует иерархия) примеси (mixins).
Это называется «структурная типизация». Она меняет проблему порождения подтипа стороннего типа, на проблему семантического конфликта структурно идентичных интерфейсов. Грубо говоря: волк одевает шкуру овцы, блеет как овца, а когда вы уходите — выплёвывает сено и съедает настоящую овцу. Оба решения так себе. Я бы предпочёл номинативную типизацию с возможностью ручного приведения к структурно идентичному типу.
UFO landed and left these words here
UFO landed and left these words here
Ну я так понимаю, что в связке с шаблонами это позволяет достичь эффекта, похожего на тот, что есть в Go. Расширения типов стандартной библиотеки без правки кода непосредственно в ней.
UFO landed and left these words here
UCS очень спорная фича — там даже в коммитете не все до конца убеждены. Это введет кучу новых правил на resolution. Но после истории с концептами, с Бьёрном предпочитают соглашаться.
UFO landed and left these words here
UCS вносит интересные вещи в resolution методов. Хорошее обсуждение тут и было еще одно где-то. Например случай с вызовом swap внутри класса, который и сейчас не очень тривиальный.

Касаемо концептов — изначально предлагался еще и другой вариант, т.к. за концепты билось несколько предложений. Тот был более выразительный, но и более сложный к реализации. И в связи с тем, что с ним возились уже несколько лет к C++0x(будущий C++11) стандарту, Бьёрн раскритиковал и порезал эту идею — вынеся части в Concepts Lite(теперь уже просто Concepts) и сказав, что хватит. Части которого, впрочем, содержали скорее его идеи. Было ли это оправдано, или нет — большой вопрос, т.к. даже облегченные концепты не смогли привезти к 14ому стандарту.
UFO landed and left these words here
А что будет, если у нас есть одновременно реализация swap как метода и swap как свободной функции? При этом на входе одни и те же аргументы (this неявно же передается).
UFO landed and left these words here
Именно — и то и другое накладывает список требований к поведению передаваемого объекта, но не требуя изменения его структуры. Побочным явлением того, что у Go это реализовано в рантайме является информация о типах во время исполнения и полноценный reflection с (почти) всеми фичами. Ну и минус это некая потеря в скорости исполнения, конечно — близкая к vtable. Такой подход не соответствует плюсам, где ты не платишь за то, что ты не используешь. С другой стороны споры вокруг RTTI все еще не прекращаются.
UFO landed and left these words here
В смысле будут ли расширятся интерфейсы? Никто не знает — все возможно, определенные изменения в язык внести были предложения, но 1) пока нет достаточного мотивации со стороны сообщества и ядра 2) жесткая обратная совместимость в пределах 1ой версии 3) цель на простоту языка — а каждая новая фича это увеличение сложности.
Сейчас усилия сосредоточены на оптимизации итогового кода и поддержки сторонних платформ. Ядро сообщество сосредоточено на улучшении GC и Escape Analysis. Впрочем с учетом итерации — релиз раз в 6 месяцев — что будет через пару лет пока предсказать сложно. У плюсов например итерация 3 года.
Затем, что это единственный способ абстрагироваться от типа обрабатываемых данных. interface{} в Go, это как void* в С.
В языках с более развитой системой типов для этого существуют дженерики, но в Go они нинужны, это все знают.
void* в С не содержит информацию о типе переменной лежащей внутри, в отличии от Go, поэтому гарантированно узнать, что же там лежит внутри можно только если у вас любая структура содержит вспомогательное поле о ее типе
interface{} это не спец тип — это обычный интерфейс, у которого нет требований по методам, а значит ему удовлетворяет любой тип. Сама по себе любая переменная интерфейсного типа в Го, это всегда пара указателей — один на саму переменную в памяти (далеко не всегда в хипе) а другая указатель на ее настоящий тип.
В языке со строгой статической типизацией делать переход от специфичного множества к множеству, включающему всё, — это путь к ошибкам. Выше вы упоминали про кодогенерацию, можете пример дать?
Никто не говорил, что это красивое или хорошее решение. Насчет кодогенерации — пример (именно пример использования) от создателей.
У меня вот такой вот вопрос. Как вообще нормально с Map работать? Там же нету метода из серии pushAll. Я правильно понимаю, что для каждого отдельного случая перекладывания данных из одного Map в другой я должен реализовать свой собственный метод?
Я правильно понимаю, что для каждого отдельного случая перекладывания данных из одного Map в другой я должен реализовать свой собственный метод?

Метод? Вам достаточно в цикле переложить:
for key, value := range map1 {
    map2[key] = value
}

Если сильно часто делаете подобное (интересно, зачем?), то можете создать функцию-обертку, да.
UFO landed and left these words here
Это даже преждевременной оптимизацией не назовешь, настолько это неуместно )
UFO landed and left these words here
Go использует hashmap в качестве реализации map. Чуть подробнее вот здесь. Знание Go не обязательно, разработчики компилятора много усилий вкладывают в комментирование кода и обоснование тех или иных решений.

А вот здесь есть более подробное обсуждение где рассказывается, что может быть от O(1) до O(N log N) — зависит от сложности ключа и количества его возможных вариантов (конечное\бесконечное).
UFO landed and left these words here
Ну вот есть у меня задача объединения двух Map'ов. Они бывают разных типов. Прошу прощения, метод в Java называется putAll, а не pushAll, конечно же. Но в джаве-то есть дженерики и этот метод принимает на вход другой Map только правильных типов, а в го так аписать нельзя. В каждом месте мне придётся городить вот этот вот цикл. Именно потому что функцию обёртку я написать не могу, потому что Map'ы разных типов у меня.
Такое бывает у программистов, пришедших с других языков, когда они сражаются с языком, пытаясь насадить свои привычные паттерны и подходы к новому языку. Но, как правило, это не длится, даже в самом упоротом случае, больше двух недель.
Вы наверное еще не пробовали Rust, раз думаете, что двух недель хватит всем. :D
Вы наверное еще не пробовали Rust, раз думаете, что двух недель хватит всем. :D

Ну, это звучало в контексте Go, а не любых языков :)
Да… такая оживленность была только в комментариях к Оберону.

Насчет generic\templates все очень просто — разработчики компилятора уже давно сами сказали, что не знают как его правильно сделать. Потому, что сделав такую вещь один раз — потом от нее уже не откажешься.
Вариант C++ — полноценные шаблоны — не устраивает потерей скорости компиляции (которая является фичей Go) и существенным ростом сложности компилятора. D использует подобный подход, но иначе относится к SFINAE и имеет полноценную среду на этапе компиляции, что тоже является проблемой.
Вариант Java — generic с потерей типа на этапе выполнения — не устраивает этим самым компромиссом. После такого типы map[string]string и map[string]notstring станут эквиваленты, что усложнит работу type switch в Go и вообще является плохим решением.
Вариант C# — generic без потери — возможен только в C# — шаблонизированный код остается таким даже в IL и превращается в конечные реализации только на этапе первого запуска функций, так как у шарпа нет интерпретатора (в отличии от явы) и JIT сработает при вызове, а не после определенного числа итераций. Поэтому затраты на компиляцию растягиваются по времени выполнения программы. В го используется компиляция для всего на этапе сборки.

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

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

На самом деле, внутри компилятора, уже есть протипы обобщенных типов (map\slice\chan) и обобщенных функций(make\new), но они требуют к себе особого пиетета и используют несколько достаточно грязных, для обобщенного программирования, трюков. Все это доступно в исходниках, для тех кто желает ознакомится. Во вторых есть go:generate, а в go 1.6\1.7 хотят переделать пакет ast, что бы им стало приятно и удобно пользоваться для кодогенерации — это было и остается хорошим решением для языка, и продвигается ядром сообщества. Для тех кому не нравится кодогенерация, есть примеры обобщенных функций с использованием reflection.

Наконец последнее — по опыту работы тех, кто берет го, необходимость в них возникает достаточно редко. Почти никто из крупных игроков использующих язык (такие как cloudflare, amazon, dropbox) жаловались совсем на другие, чаще всего детсткие, проблемы языка. А facebook выпустил вот такой «пакет» (осторожно юмор).

Главная фича Го — асинхронность сияет не на каналах, а на неблокирующем IO которое программист видит и пишет как блокирующее. Большинство блокирующих I/O операций реализованы внутри с использованием epoll\WaitForMultipleObjects в специальной горутине. Этот подход близок к подходу C# c async/await, но за счет того, что у горутин полностью свой планировщик, есть определенные преимущества по скорости и возможным оптимизациям, а самое главное не приходиться явно указывать, что функция асинхронная. В случае когда есть необходимость в своей, нативной, блокирующей операции и необходима скорость, неплохим решением будет прикрепить (такое возможно) горутину к потоку ОС и сделать из нее менеджера раздающего данные по каналам. Хотя опять же, необходимость в таком возникает достаточно редко.

В заключении — у Go хорошая и легкая спецификация. Изучается язык буквально за пару вечеров — целиком. Поэтому обоснованность решения в пользу или против го для каждого проекта стоит сделать самостоятельно. По словам Роба Пайка — одного из создателей языка — простота и некая топорность была ключевым требованием т.к. требовалось, что бы новые программисты могли как можно быстрее входить в курс дела. Как тут насмешливо сказали «язык для второкурсников» — и это недалеко от истины. Плохо ли это? Как показал опыт больших компаний и успех явы — элегантные и красивые решения чаще всего не самые удачные, стоят много денег и их тяжело поддерживать. Хотя и звучит это довольно грустно. Впрочем никто не собирается выкидывать существующие языки из тех сфер где они действительно применяются правильно. Даже гугл лишь добавил го к списку одобренных внутри компании языков — у них по прежнему есть новые проекты на C++\Java\Python.
«Полноценные шаблоны», они же «гетерогенная компиляция обобщённого кода» — это оптимизированная версия кодогенерации, инкапсулированая в компиляторе. Так что go:generate и go:ast не может быть быстрее толково реализованного go:generics. Другое дело, что для этого нужно приложить чуть больше умственных усилий.

Отрывок из книги «Язык программирования D», от Александреску:

В настоящее время наиболее распространены два подхода к генерации кода для параметризации типов:

• Гомогенная трансляция: все данные приводятся к общему формату, что позволяет скомпилировать единственную версию find, которая подойдет всем.

• Гетерогенная трансляция: при каждом вызове find с различными аргументами типов (int, double, string и т.д.) компилятор генерирует отдельную версию find для каждого использованного типа.

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

Гетерогенная трансляция, напротив, отличается специализированностью, выразительной мощью и скоростью сгенерированного кода. Плата за это – распухание готового кода, усложнение языка и неуклюжая модель компиляции (обычный упрек в адрес гетерогенных подходов – что они представляют собой «возвеличенный макрос» [вздох]; а поскольку благодаря C макрос считается чем-то нехорошим, этот ярлык придает гетерогенной компиляции сильный негативный оттенок).

Тут стоит обратить внимание на одну деталь: гетерогенная трансляция включает гомогенную по той простой причине, что «один формат» входит в «множество форматов», а «одна реализация» – в «множество реализаций». На этом основании (все прочие спорные моменты пока отложим) можно утверждать, что гетерогенная трансляция мощнее гомогенной. При наличии средства гетерогенной трансляции ничто не мешает, по крайней мере теоретически, использовать один универсальный
формат данных и одну универсальную функцию, когда захочется. Обратное, при использовании гомогенного подхода, просто невозможно.

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

D использует гетерогенную трансляцию (внимание, ожидается бомбардировка техническими терминами) с поиском статически определенных идентификаторов и отложенной проверкой типов. Это означает, что, встретив определение обобщенной функции find, компилятор D выполняет синтаксический разбор ее тела, сохраняет результаты, запоминает место определения функции – и больше ничего, до тех пор пока кто-нибудь не вызовет find. В этот момент компилятор извлекает разобранное определение find и пытается скомпилировать его, подставив тип, который инициатор вызова передал взамен T. Если функция использует идентификаторы (символы), компилятор ищет их в том контексте, где была определена эта функция.
Если мне не изменяет память — то D даже позволяет генерировать код из текстовых констант. Вплоть до вставки mixin'ов из текста. Это может стать красивым решением в руках действительно хорошего разработчика, или жутких кошмаром в руках неопытного, и это тяжело читать и отлаживать. Даже boost покажется легким развлечением при попытке отладки такого кода.
Второе — такие слабые ограничения на времени компиляции делают невозможным серьезный code-assist и хороший автокомплит. Насколько я знаю — эту проблему до сих пор не решили.

Разницы скорости итогового кода при грамотных «заготовках» в случае go:generate не будет — как и при грамотных шаблонах. А если мы говорим про скорость компиляции, то такое сравнение не совсем корректно — т.к. условия на вызов кодогенерации можно повесить куда более сложные, чем на шаблоны. Однако это вызывает необходимость следить за изменением кода уже самому\с помощью сторонних средств.

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

П.С. К Александреску, даже в мире плюсов с использованием шаблонов на полную катушку, относятся чуть с поднятой бровью. Идея с метапрограммированием хороша ровно до тех пор, пока вы понимаете, что из зачем пишет ваш коллега. У Александреску коллега это Уолтер Брайт — ему повезло…
> такие слабые ограничения на времени компиляции делают невозможным серьезный code-assist и хороший автокомплит

https://habrastorage.org/files/d1e/52b/7c4/d1e52b7c487b4587a6315aa8c8dd48a0.png

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

Например?

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

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

> К Александреску, даже в мире плюсов с использованием шаблонов на полную катушку, относятся чуть с поднятой бровью.

Потому что к плюсам метапрограммирование было прикручено сбоку, а в D вокруг него строился весь дизайн языка. https://gist.github.com/nin-jin/e52e51a68e5a6b17c6fb#file-template-mixin-runtime-error-d

Articles