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

Не такая она и ужасная. Местами очень даже полезная, взять тоже Thrift. Всему есть место, кроме копипасты. Копипасте тоже есть место, но очень редко и в исключительных случаях.

Он же вроде для междуязыковой компиляции. А зачем такое в рамках одного языка?

У нас в проектах java immutables (которая практически кодогенерация) крайне помогает. Кроме того практически весь современный front-end js построен фактически на кодогенерации (babel, es6 support, react jsx)
Что ни разу не облегчает отладку)
Про java immutables — попробуйте, там всё более чем прозрачно и отладка становится даже легче (!) за счёт легко предсказуемого поведения — речь об иммутабельных структурах всё-таки.

Про babel/es6/jsx — есть же source maps, один раз на проект настроил и отлаживай себе.

А еще есть lombok. И мне почему-то, кажется, что оба эти проекта подходят под пункт: "Мы боремся с косяками языка".


А с js ситуация еще хуже, потому что там у людей еще и выбора нет.

Да, согласен! Но во всех этих случаях использование ванильных языков для чего-то большого приводит к на порядок-два большим затратам усилий и времени.
Современный «front-end js» это настоящий ад, между делом.
Да нормально, можно ехать.
image
И потом этот «кодосгенерированный» front-end js адски тормозит на клиентских устройствах.
Интересно посмотреть ссылки на исследования, которые бы подтверждали Ваше утверждение. «кодосгенерированны» front-end js — из какого языка (js, ts, coffeescript, purescript, что-то ещё)? Каким генератором (babel? с какими плагинами? с какими настройками? tsc? jsx использовался? ...) Речь про телефоны? Планшеты? Ещё про какие клиентские устройства? «Адски тормозит» — это на порядок медленнее?

Без ответа на эти вопросы исходное утверждение выглядит как «кит сильнее слона».
Клиент и сервер могут быть написаны на одном языке, но быть независимыми проектами и разрабатываться разными командами. И тогда Thrift будет их стыковать. Т.е. и клиент, и сервер будут зависеть от некоторого третьего продукта, который будет представлять собой набор файлов Thrift.
Что мешает в таком случае, если использование других языков не планируется, использовать не Thrift, а возможности языка и написать на нем ядро для обоих частей?
Ничего не мешает, это просто два разных подхода к решению одной проблемы. В каком-то одном случае будет удобен один вариант, в другом — другой.
Если ядро призвано только синхронизировать интерфейсы взаимодействия, то, возможно, проще будет поддерживать консистентность используя кодогенерацию, так как она обеспечивает меньшую свзяность проектов между собой. А если должна быть еще какая-то общая бизнес-логика, то лучше писать ядро.

Возможно, вы и правы. Мне кажется, что все-таки удобнее на одном языке, но задачи бывают разные.

И я не являюсь сторонником кодогенерации в Go, просто Вы одним махом записали всю кодогенерацию, как нечто ужасное.
А если вернуться к Go, то она еще и реализована довольно не удобно — ее нужно запускать каждый раз в ручную (по крайней мере из того, что я слышал).
Если уж кодогенерацию использовать, то модификация исходников для кодогенерации должна автоматически приводить к перегенерации кода в момент компиляции, а этого в Go нет.
Кодогенерация считалась всеми простой, понятной, и в общем-то часто используемой вещью до появления IDE… которые её приватизировали.

Разработчики Go — не были фанатами IDE и потому использовали «старый стиль», когда кодогенерация является одним из инструментов, а не «табу», с которым борются всеми возможными способами…

Это где же в современных ide кодогенерация?


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

Могу вспомнить как минимум resource файл в VisualStudio. Можно статично через диалоговое коно указать путь к файлу. IDE ещё на прекомпиляции проверит что такой файл существует и сгенерирует статичный класс. Очень удобно например вынести все sql файлы в такой ресурс, а потом в месте использования заявки написать что то вроде:
var result = await db.Query(SQLResources.GetProducts);
Это где же в современных ide кодогенерация?
Везде. Начиная от автоматического создания кучи бойлерплейта Wizard'ами, до вполне себе «классической» кодогенерации вещей типа R.java.

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

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

Я так не считаю. Разве что в случае с ресурсными файлами, как мне указали правильно, и, например, настройкой GUI через стороннее приложение.


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

Разве что в случае с ресурсными файлами, как мне указали правильно, и, например, настройкой GUI через стороннее приложение.
А чем ресурсные файлы уникальны? Чем ORM, RPC, FSM хуже? Тем, что ваша IDE их не знает? Ну так научите! Или успокойтесь уже и признайте, что «магическая лицензия» для кодогенерации не нужна.

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

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

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


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

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

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


if err != nil {
    return nil, err;
}
Кодогенерация вместо дженериков в языке — это, несомненно, костыль. Но неясно, насколько велика проблема. Мне, на практике, дженерики не были так уж сильно нужны и особых проблем я от необходимости поддерживать их руками никогда не испытывал. Возможно в тех, проектах, которые делаете вы — ситуация другая, было бы интересно посмотреть на проект, где дженерики занимают столь существуенную часть, что их отсутствие реально мешает.

Берите любой крупных проект на go и попробуйте сделать grep "interface{}" -R . в его репозитории. Я не эксперт, но мой взгляд, почти каждый interface{} будет из-за проблемы с отсутствием дженериков. Я вот только что грепнул alertmanager и prometheus. И даже в alertmanager нашлось место этому чуду:


./template/template.go:func (t *Template) ExecuteTextString(text string, data interface{}) (string, error) {
./template/template.go:func (t *Template) ExecuteHTMLString(html string, data interface{}) (string, error) {
./template/template.go:type FuncMap map[string]interface{}
./template/internal/deftmpl/bindata.go:func (fi bindataFileInfo) Sys() interface{} {
Go подход к ошибкам просто не работает, если не ошибусь, товарищи авторы языка об этом в курсе и задача переосмыслить и предложить новый подход к работе с ошибками значится в задачах go 2.0

Я всегда считал, что наоборот: код, сгенерированный визардами и т.п. — это плохо. Во первых, если внести в него изменения — при повторной генерации они пропадут. Во вторых, сгенерённый код не должен попадать в version control. В третьих, это вообще что-то вроде obj-файла, в нормальных условиях мне не надо его видеть.
Короче: кодогенерация на этапе сборки — нормально, кодогенерация при написании кода — фу-фу-фу.


Ну а "считается" — вроде как считается, что стандартные инструменты кодогенерации лучше самописных, поэтому мы наворачиваем горы темплейтов в C++ там, где код мог сгенерить скрипт из пяти строк...

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

Когда хочется написать чтото действиетельно сложное, (пытаясь простыми средствами реализовать сложную конструкцию или недостающую) то обычно простой код может превратиться в очень труднопонимаемый.
Попробуйте язык D, который приводится в качестве примера в статье. Нативный код с GC, нет зоопарка разных компиляторов. Я использую его для решения задач из HackerRank.com и мне D показался отличным языком для написания алгоритмов.

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


Компилятор, как и stdlib всё еще имеют баги, которые препятствуют использованию D в продакшне. Увы, это язык скорее "для души" нежели для работы.

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

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

У меня есть крупный проект клиента MMORPG на OpenGL на D.
В начале разработки(4-5 лет назад) постоянно натыкался на баги в компиляторе/стандартной библиотеке. За последний год если только несколько багов в новой функциональности нашёл.
Единственная проблема крупная на данный момент — консервативный GC, который в моём клиенте умудряется течь, чем несказанно портит жизнь.
Главная проблема комьюнити — они любят трепаться о всяких новых технологиях, но никто не хочет тот же самый GC сделать precise.

У Вас клиент наверное под x86 без 64?

Под x86_64.

Странно что он течет под x86-64, т.к. по идее проблема "фальшивых" указателей на этой платформе стоит не так остро как на 32 разрядных системах.
Жаль это слышать, надеюсь они наконец-то дотестят и сольют пулл реквест precise gc с мастером.


А сколько примерно объектов в памяти у Вас в среднем? Просто для статистики, хочу лучше понять масштаб проблемы уктечек на стандартном GC

issues.dlang.org/show_bug.cgi?id=18005
Как можно видеть, даже создание 1 массива без указателей заставляет приложение течь на Windows особенно.
Точное число я не измерял, но из-за этого приложение быстро зажирает под несколько гигабайт, даже если активных данных — 90 МБ.
А как оно будет через 10 лет? Сможете скомпилировать и запустить код, написанный сегодня?

Это один из важгейших вопросов — но я не понимаю что разработчики D вообще про него думают…

Можете попробовать Nim, там тоже нативный код с опциональным GC, к тому же ведётся работа над деструкторами, кросс-компиляция как в Си (Nim компилируется в Си), никаких виртуальных машин и максимальная производительность (на уровне близком к чистому Си при правильном использовании языка)

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

Автор не понимает целей и задач Гугла при разработке этого языка, поэтому сетует на то, что Go — это такой «С для дураков», а надо было вместо него внедрять что другое, с нативным ООП, с шаблонами, с выводом типов. D, например, или Rust, или вообще ЯФП какой-нибудь.
Так вот, примерно 90% задач гугла вполне решается на этом самом «С для дураков» без использования кодогенерации, рефлексии, шаблонов, ООП и чего либо еще, и самое главное, что в итоге написанный разработчиками любого уровня код на Go смогут понять любые другие разработчики, даже если они в компании работают сутки и пришли сразу после ВУЗа.
Более того, при уходе всех оригинальных разработчиков из команды проект не придется выбрасывать весь, потому что в нем никто не может разобраться, а получится передать другой команде обычных программистов, и они смогут подхватить его в относительно короткие сроки.
Простота поддержки, простота отладки и поиска ошибок, доступность даже для новичков, и минимальные шансы выстрелить себе в ногу — вот что нужно компаниям уровня Гугла для большей части их кода. А оставшуюся меньшую часть можно писать на чем угодно, хоть на ассемблере, если есть такая необходимость.
примерно 90% задач гугла вполне решается на этом самом «С для дураков»

Откуда такая цифра «90%»? Всякие intelligent personal assistant'ы, беспилотные автомобили и другие AI-проекты, которыми так годится Гугл, входят в эти 90% задач? А Google Chrome?
Грубая оценка по опыту работы в другой большой компании похожего уровня.
Из всего вышеперечисленного только Chrome уже написан, и браться его переписывать на Go с нуля нет никакого смысла, все остальное — почему нет? Для хардкорной математики все равно будут использованы готовые библиотеки на С или Фортране (FFI у языка есть, сопряжение хоть и не без проблем, но работает), а для основной grunt work язык Go вполне подходит. Они на нем теперь даже initramfs пишут, так что я не удивлюсь, если напишут и что-нибудь из ML (AI все же слишком сильный термин, на мой взгляд).
Из всего вышеперечисленного только Chrome уже написан, и браться его переписывать на Go с нуля нет никакого смысла
Помимо смысла: писать хороший GUI без богатого ООП очень трудно, хотя и возможно.
AI все же слишком сильный термин, на мой взгляд
Если assistant по замыслу не просто «болталка», вроде Элизы, то AI там нужен мощнее, чем сегодня доступен. Т.о. существующих библиотек явно недостаточно. Нужны собственные научные разработки. Аналогично про беспилотный автомобиль. (Или есть способ прятать внутри живого пилота :)
Помимо смысла: писать хороший GUI без богатого ООП очень трудно, хотя и возможно.
Вот как раз тот ООП, который есть в Go для написания GUI подходит куда больше, чем «классический».

Но GUI в Chrome в основном на JavaScript всё равно, так что тут скорее проблема в специфике продукта, а не в том, что GUI на Go сложно писать…
в Go для написания GUI подходит куда больше

Когда в руках молоток, все вокруг — гвозди
Причём тут молоток? При создании GUI вам регулярно нужно связывать между собой разнородные обьекты.

Что в модели ООП пропогандируемой С++/C#/Java сделать не так-то просто: недаром все C++ библиотеки так или иначе завязаны на кодогенерацию. Явную (как в MFC или Qt) или неявную (как в WTL — через шаблоны). В CLOS этой проблемы нет и, собственно, своременный GUI разрабатывался на этой обьектной модели ещё до того, как его увидел Jobs.
Не совсем понимаю что такое «связывать между собой разнородные объекты». Не могли бы вы пояснить какие именно связи имеются в виду?
Вот те самые, которые в Qt делаются через кодогенерацию, а в MFC и других подобных тулкитах — через ещё более сташные извращения.

ООП в стиле CLOS, принятый в Go решает эту проблему абсолютно естественным способом: так как интерфейсы и их реализация напрямую не связаны с типами, которые их реализуют, то страшные трюки в стиле WTL (когда у вас обьект-предок приходится делать шаблонным, чтобы он мог как-то обратиться к потомку) не нужны.

Впрочем, как я уже сказал, полноценных библиотек виджетов, сравнимых с Qt для Go пока нет, так что сказать на 100%, что всё пройдёт гладко и удастся обойтись без костылей типа кодогенерации (а макросы это тоже кодогенерация, пусть и примитивная) — пока нельзя.
В таком случае я не понимаю претензий к C#, где то же самое является частью языка (аналог сингала — это событие, слотом может выступать любой метод подходящей сигнатуры) безо всякой кодогенерации.
Замечание принято. Да, пожалуй в C# этой проблемы нет. Там среди прочего всякого добра напихано и решение этой проблемы тоже.
Так вот, примерно 90% задач гугла вполне решается на этом самом «С для дураков» без использования кодогенерации, рефлексии, шаблонов, ООП и чего либо еще, и самое главное, что в итоге написанный разработчиками любого уровня код на Go смогут понять любые другие разработчики, даже если они в компании работают сутки и пришли сразу после ВУЗа.

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

простота отладки
В копипасте?

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

Ну да, как же… Gotchas and common mistakes in Go
По количеству WAT Go может влёгкую побороться за второе место после JavaScript.

По количеству WAT Go может влёгкую побороться за второе место после JavaScript.
Только за третье и то не факт. Первое-второе места забиты намертво за JavaScript'ом и PHP, которые всё никак не выяснят какой из них кривее.
Хотите сказать кто вообще способен хотя бы догнать С++ и PL/1? Что-то во времена студенческие я не видел ни одного учебника по С++ (в особенности от авторов языка) в котором хотя бы 4/5 примеров компилировались, а с тех пор в язык только добавляют и добавляют новые способы стрельнуть себе в ногу.
Что-то во времена студенческие я не видел ни одного учебника по С++ (в особенности от авторов языка) в котором хотя бы 4/5 примеров компилировались
Если что-то не скомпилировалось — так это ж хорошо: вы увидите проблему и её исправите. Это вообще не проблема.

А вот если оно скомпилировалось и делает не то, что ожидалось… это бяда. В этом отношении JavaScript и PHP не переплюнуть… хотя C++ и старается, да.
Пример-то я исправлю, но я не могу исправить учебник. А кто-то по нему учится…
Если что-то не скомпилировалось — так это ж хорошо: вы увидите проблему и её исправите. Это вообще не проблема.

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

Для человека, который свой первый язык учит по этой книге, которая для него основной источник информации и об языке, и о программировании вообще, это может быть большой проблемой.
Для книги и обучения — да, это проблема. Для языка — нет. И показателем качества языка это не является ни разу (показателем качества книги — таки да).

Если в книге 3/4 примеров не компилируются или крашатся не из-за ошибок автора-издателя, а из-за того, что раз в год в языке происходят сильные изменения, ломающие обртаную совместимость, то к качеству языка это имеет прямое отношение.

Несомненно. Но с учётом того, что у меня есть знакомый, который отличненько осваивал курс, написанный под Turbo C 2.0 с использованием последней версии GCC и Clion… я думаю что проблема-таки в издателях… Ну или в горе-программистах, которые в учебных примерах используют всякие <conio.h>, которые большинством компиляторов никогда не поддерживались.
В этом отношении JavaScript и PHP не переплюнуть… хотя C++ и старается, да.

Не знаю за плюсы, но указатели в чистом С- это такой реактивный многозарядный гранатомёт со сверхчувствительным спусковым механизмом, по сравнению с которым сравнение строк в этих скриптовых языках- полнейшая фигня.
Теоретически — да. А практически — в типичном web-магазинчике будут десятки миллионов строк кода на C/C++ (не только ядро операционной системы, но и всякие SQL-базы данных, акселераторы и прочее — это ведь всё на C/C++ написано), а сверху — тысяч сто строк на PHP или Ruby. При этом на каждую дыру в ядре будет пара SQL-уязвимостей в движке.

Так что да — указатели они, конечно, ужасны и опасны, но до уровня JavaScript/PHP — не дотягивают…
Вы серьёзно сравниваете вылизанный код ОС, интерпретатора и БД, используемый на миллионах инсталяций, с уникальным наколеночным кодом студента на подработке (утрирую)?
Это был толстый намёк, что даже авторы стандарта не знают его от и до и (sic!) раз даже простой пример набрать без ошибок не могут. А без полноценного понимания языка говорить о совместимости его функций (= отсутствии косяков в дизайне) как-то смешно.
В этом смысле php намного лучше — пространства для косяков меньше, дефекты дизайна постепенно исправляются, глупости перестают интерпретироваться (в отличие от С++).
И кто теперь более читабельный? Я отдам свой голос D. Его код куда более читаемый, так как он более явно описывает действия.

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

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

Есть такая вещь как reproducible builds. Вендоринг зависимостей с этим совместим, а там уже можно уже выбирать — копировать или сабмодулем делать. Просто импорты со ссылками на репозитории в надежде, что во время билда все это успешно скачается и соберется — нет.

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

Такое ощущение, что автор статьи студент, которому лишь бы поиграться с языком, в котором побольше фич. Человек будто совсем не понимает задач компаний вроде Гугл.
По поводу билдов немного странный аргумент. Почему нельзя было сделать нормальную систему пакетов, как смог тот же Nim или Crystal? Версии по коммитам это просто ад.
Потому что нормальная система пакетов обычно зависит от стороннего сервиса, на который надеяться себе же дороже. Не так давно уже была забавная история с node.js что ли, когда удалили безобидный пакет. Банально интернет может не работать и что, вся компания будет сидеть и ждать его возвращения? В конечном итоге приходит понимание, что наверное стоит поднять тот же самый сервер локально, чтобы он хотя бы кэшировал зависимости. Либо окончательное прозрение, что стоит все зависимости тащить с собой в репозиторий в отдельной папочке «vendor». Судя по всему, компании уровня Гугл и даже куда меньше именно к последнему и приходят. Конечно, не идеал и не удобно бывает, но вариантов других особо нет.

А как эти две вещи связаны? Храните где хотите, но отвяжите пакеты от VCS и заставте людей делать нормальные пакеты с версиями. Вам же его скачать то все равно с внешнего сервиса нужно.

Видимо, гугл считает, что им удобнее вот так. У них, к примеру, весь C++ код хранится в едином репозитории и собирается целиком. Казалось бы, милое дело — выделить модули, каждый модуль разрабатывать независимо в своем репозитории (и секюрность, и размер репо и билда уменьшить, и команды друг другу мержи и билды ломать не будут), стабильные оттестированные релизы пакетировать и выкладывать на тот же внутренний сервер — красота, все по святцам! Но нет, гуглу оказалось объективно лучше держать все в одной куче и жить на "острие".
Есть такая поговорка "ты не гугл". То, что может казаться (и быть) хорошим и приятным для нас с вами, для компании такого размера может был плохим и вредным. В частности, таким вредным они считают версионирование пакетов. Что логично — нетрудно обеспечить совместимость версии, если у тебя собирается пакет, зависящий от еще пары. Когда у тебя собираются тысячи и десятки тысяч пакетов, зависящих друг от друга, то менеджмент совместимых версий превращается в ад.

Когда у вас так много пакетов, у вас все равно ад.


Лично мне кажется, это потому, что google в целом может положить большой болт и тестировать новый функционал на 10% клиентов. Далеко не все компании так могут, так как не монополисты все-таки :)

размер билда? В плюсах же по идее ты не платишь за то что не используешь.

Я о суммарном размере билда. Если кто-то изменяет библиотеку в модульной архитектуре, то им надо только проверить, что она собирается; сборка, проверка вышестоящих библиотек — это задача тех, кто будет обновлять зависимости. Если кто-то изменяет библиотеку в гугле, то им надо собрать всё, что ее использует, и поправить всё сломавшееся. Гугл, НЯЗ, использует виртуальную ФЗ (как ClearCase MVFS, чтобы не убивать место на HDD и время программистов миллионами файлов из репо), распределенные билды и хитрые билд-системы, чтобы скорость билдов была адекватной, но билды все равно должны быть сделаны и артефакты все равно должны куда-то упасть.

Если кто-то изменяет библиотеку в модульной архитектуре, то им надо только проверить, что она собирается
А работоспособность кто проверять будет? Пушкин?

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

Когда у вас между проектами — административная граница, то у вас, в общем-то, нет выбора. Но если её нет — общий репозиторий лучше. Собственно все большие проекты так всегда и собирались — и UNIX, и OS/360 и многие их последователи.

Только GNU/Linux, по политическим причинам, устроен не так. Ну так то — вынужденная мера, а не то, к чему стоит стремиться.

но билды все равно должны быть сделаны и артефакты все равно должны куда-то упасть.
А в случае с несколькими проектами и несколькими репозиториями это не так?
А работоспособность кто проверять будет? Пушкин?
Подразумеваем, что работоспособность библиотеки проверяется. Работоспособность или даже собираемость других библиотек — нет.

Но если её нет — общий репозиторий лучше.
Я с этим спорю, что ли? Я говорю, что для "нас с вами" (use-case типичный опен-сорс проектик) удобнее релизы и версионируемость. Для гугла — удобнее монорепозиторий и жизнь на "голове". Go — язык гугла для гугла; логично, что они использовали для зависимостей в Go ту же философию. Поэтому и статья в целом ("мне Go не нравится, а я знаю D умный программист => Go не для умных программистов"), и тот комментарий, на который я отвечал ("для меня пакеты удобнее => Go нужны зависимости в пакетах, а не голова репозитория"), мне кажутся наивными. Я попытался намекнуть, что у гугла может быть точка зрения, отличная от ТЗ типичного Васи Пупкина, но это не оценили.

А в случае с несколькими проектами и несколькими репозиториями это не так?
Не так, по той причине, что не надо проверять собираемость вышестоящих библиотек. Грубо говоря, в "модульном" варианте выпуск новой версии Abseil влечет за собой билд библиотеки Abseil и прогон ее автотестов. В монорепозитории — (инкрементальный, но) ребилд всего репозитория из тысяч проектов, и прогон всех автотестов всех этих библиотек.
С версиями вот такие рассуждения.

У нас есть некий код, допустим он типизирован и есть внешний интерфейс у импортируемого модуля.
Что в таком случае будет означать изменение версии?
Изменение API модуля? Совсем не обязательно (в semver это предполагает лишь в мажорных версиях). А что тогда? Непонятно, все равно придется проверять, что же изменилось вручную.
Получается, что система версий не типизируется, а значит не автоматизируется.
А значит, весь вопрос уже заключается в том, как вести для людей лог изменений модуля, и я вовсе не уверен что безликие 3 циферки что-то значат.
И логика без цифр такая.
Если это внутреннее изменение (ну, фикс безопасности, к примеру), то и не нужны никакие новые цифры. Если изменение API, то, опять же, компилятор должен ругнуться. Если изменения API нет, но поведение изменилось — нужно менять название пакета, а старый оставить в покое, обратная совместимость, все дела.
Привязка же к коммитам удобна при параллельной разработке, но внутри одной компании.

А эти все куча цифр в некоторых пакетных менеджерах — попытка костылем подпереть отсутствие статической типизации. Все равно не работает.

Версионность выполняет ровно две функции:


  • Человекочитаемость. Когда я вижу, что пакет версии 2.1.5, а я использую версию 0.5.1, то мне надо обновится (и вполне вероятно, поиметь проблем). А когда я вижу, что использую коммит 249f069bdf09be30258c604be27fdce51694706f вместо 249f069bdf09be30258c604be27fdce51694706f это мне ни о чем не говорит.
  • Исправность. Есть такая штука, что далеко не у всех людей каждый коммит содержит код, который вообще собирается. А каждая версия программы должна собираться, без этого никак.

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


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

Вы специально одинаковые хэши написали?
Хм… нет, криво скопировал.
Есть такая штука, что далеко не у всех людей каждый коммит содержит код, который вообще собирается.
И вот «с этой штукой» и нужно бороться. Trybots для этого и существуют.

Более того, версионность позволяет не только жестко фиксировать версии, но так же и задавать диапазоны версионности, что бы у вас не возникало по три-пять разных версий пакетов в одном и тот же проекте.
Откуда у вас возьмутся «три-пять» версий пакетов (и вообще пакеты, как таковые) при использовании одного репозитория? Или вы про master/release/release-stable и т.п. ветки? Ну так они «внутри себя» все согласованы…
Откуда у вас возьмутся «три-пять» версий пакетов (и вообще пакеты, как таковые) при использовании одного репозитория? Или вы про master/release/release-stable и т.п. ветки? Ну так они «внутри себя» все согласованы…

Давайте начнем с простого. Если вы работаете в google/facebook/another company где все проекты лежат в одном репозитории (на самом деле в google и facebook это уже тоже не так, у них есть open source) и вообще никак не завязаны на open source либы — я за вас рад.


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

на самом деле в google и facebook это уже тоже не так, у них есть open source
Угу — и это позволяет посмотреть как всё устроено.

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

Когда вы скачиваете себе два разных проекта к себе в репозиторий, то они обязаны работать с той версией пакетов, что у вас есть. Можете посмотреть в Chrome или Android: пакеты там не дублируются (за исключением резко патологических случаях типа AngularJS vs Angular, или Pel5 vs Perl6 где разные «версии» лучше рассматривать не как разные версии одного и того же, а два совершенно разных продукта).

Вот чтобы такую проблему не решать Go по умолчанию и берёт всё из trunk'а. Если ваши пакеты, в результате, не заведутся и не заработают — ну да, можно попробовать накостылить что-нибудь. Но по умолчанию — все пакеты должны использовать одной версии. Самой последней.
То есть если в транке популярной библиотеки кто-то выложит новую версию, которая ломает совместимость, то по цепочке упадут все проекты, которые используют этот пакет, пока все их них не будут пофикшены?
Ну не путайте стадии. В production всегда должны фиксироваться именно те версии, на которых прошли все тесты. Любое обновление по любому поводу (даже patch) может происходить только на стадии разработки. Так что да, упадут все проекты, и встанет очевиден вопрос, с чего это используется библиотека, которая ломает обратную совместимость. Но нет, упадут не в боевой версии, а у разработчиков.
Я разрабатываю какой-то продукт и использую множество библиотек. Делаю `go get` и скачиваю из транка новые версии библиотек. Оказывается, что в одной зависимости нескольких моих зависимостей сломалась совместимость. Теперь моя разработка стоит пока все эти библиотеки не пофиксят совместимость с той, что внесла изменения?

Обращу внимание, что gitflow не отрицает изменений, ломающих совместимость. То есть в мастер вполне может прилететь такая версия.
Я разрабатываю какой-то продукт и использую множество библиотек.
И вот в этот момент — у вас уже проблемы: если вы не понимаете что вы используете и допускаете использование бог-знает-чего, то кто ж вам судья, что у вас что-то сломалось?

Обращу внимание, что gitflow не отрицает изменений, ломающих совместимость. То есть в мастер вполне может прилететь такая версия.
Ничего не знаю про gitflow. Знаю что в gerrit есть кнопочка rollback, которая откатывает изменения без вытаскивания их в клиент и прочего. Вот именно на случай, когда в trunk попало что-то, что сломает кучу клиентов.
используете и допускаете использование бог-знает-чего, то кто ж вам судья, что у вас что-то сломалось?

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

Вот именно на случай, когда в trunk попало что-то, что сломает кучу клиентов

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

Но у меня ведь нету доступа к такой кнопочке независимой библиотеки.
Сделайте клон — появится.

Но вообще идеолигия такая, да: trunk'и всех проектов должны быть совместимы.

Если какой-то проект постоянно ломает API trunk'а и заставляет использовать устаревшие версии — то от него лучше отказаться.
Ох уж этот стокгольмский синдром. Я понимаю, что язык молодой, библиотек на все случаи жизни еще не написано, сейчас принято велосипедить на каждом проекте, но ведь скоро это изменится.

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

Вообще говоря версии тут вам ничем не помогут. А поможет фиксация ревизий. Которая через git submodules реализуется куда лучше. Но это не про го вроде.

Да нет, библиотек уже довольно много.
А вот подобный радикализм действительно встречается. Мне правда не понятно до конца зачем… Может потому, что в го идут те, кто хочет забыть все сложности и «просто писать код».
Нет никакого «нормального управления пакетами». Чужой код — всегда чужой код. Хоть обвешайтесь циферками, leftpad будет вас ждать.

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

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

Но не надо обвинять инструменты, просто нужно разделять экспериментальный код, прототипы VS рабочий код.
Вот в первом случае, go get вам в руки, semver с ^ самой новой версией.
Во втором, жесткая фиксация, папка vendor с необновляемым кодом зависимостей, ну а в случае semver жесткий номер версии, вплоть до номера патча.
Еще раз, единственная причина, по которой вам может понадобиться обновить библиотеку — неожиданный критический баг. Это не требует никаких версий и уж точно не может ломать совместимость.

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

Если библиотека достойная, то ее код уже готов к использованию

это уже ваши тараканы, наверное, вы один из миллиона некачественных стартапов.

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

Хочу напомнить, что слабость синтаксиса Гоу очень сильно усложняет развитие библиотек (извините, я понимаю, что у вас в репозитории только идеальные библиотеки, которые развития больше не требуют, ибо они сразу написаны в финальной версии, но, очевидно, я из плохих стартапов и мои библиотеки не такие розовые и плюшевые). Так вот — нету ни аргументов-по-умолчанию, ни переопределения методов. В итоге, если мне надо добавить в метод параметр (ну простите, я не идеальный архитектор, не умею писать сразу финальный код) — я не могу вменяемо просто добавить в этот метод параметр не сломав совместимость.
В итоге, если мне надо добавить в метод параметр (ну простите, я не идеальный архитектор, не умею писать сразу финальный код) — я не могу вменяемо просто добавить в этот метод параметр не сломав совместимость.
Плохому танцору… Почему это не мешает разработчикам Linux (я имею в виду ядро), MacOS (я имею в виду Classic), Unix, Windows и многих других систем?

Может проблема не в слабости языка, а в банальном нежелании думать о стабильном API заранее? Ну так в этом случае никакой синтаксический сахар не поможет…
Это вы об этом?
HWND hWnd = CreateWindow(  
        szWindowClass,  
        szTitle,  
        WS_OVERLAPPEDWINDOW,  
        CW_USEDEFAULT, CW_USEDEFAULT,  
        500, 100,  
        NULL,  
        NULL,  
        hInstance,  
        NULL  
    );  


Уверен, они не рады необходимости тащить ужасное легаси и поддерживать отвратительный синтаксис подобного вида.

Конечно, я понимаю, что можно заранее добавить параметров на все случаи жизни. Можно нанять штатного телепата. Уверен, в вашем мире единорожков именно так и делают.
Уверен, они не рады необходимости тащить ужасное легаси и поддерживать отвратительный синтаксис подобного вида.
Разумеется нет. Программисты — они такие. Им всегда хочется что-нибудь переписать с нуля и всё сделать «правильно». Но когда им позволяют это сделать… технологический лидер — становится бывшим.

Конечно, я понимаю, что можно заранее добавить параметров на все случаи жизни. Можно нанять штатного телепата. Уверен, в вашем мире единорожков именно так и делают.
Причём тут «мир единорожков»? Про телепатов — не знаю, но «добавить параметров» — это путь Windows. Есть ещё путь Linux/Unix: если нам нужен параметр в dup2, то появляется dup3 (а dup2, разумеется, остаётся).

Есть много подходов, позволяющих сохранить обратную совместимость. И да, бывают случаи, когда без этого не обойтись. Скажем у GlibC вышла версия 1.0 в 1992м году, а в 1997м — вышла версия 2.0, сломавшая совместимость к чёртовой матери. Просто потому что подход, использованный в GLibC 1.0 оказалось практически невозможно использовать с ELFом. Но вот уже GLibC 2.26, вышедшая несколько месяцев назад — вполне совместима с GLibC 2.0, вышедшей в 1997м.

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

Внутренние API, в рамках одного репозитория — другое дело: тут вы не предполагаете, а знаете, причём точно знаете — что нужно вашим клиентам. В случае же с публичным API — вы можете только предполагать… и очень часто — неправильно.
Им всегда хочется что-нибудь переписать с нуля

Я не говорил про «переписать с нуля» вообще-то, это уже ваши домыслы. Поломка обратной совместимости и переписывание с нуля — это совершенно разные вещи. Ну вот как Юнити к примеру. Сколько раз там ломалась совместимость, а они до сих пор на коне?

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

То есть вы мне кинули статью, где описывается, что от Микрософта люди ушли из-за отвратительного API на более приятные технологии и шаг с значительным опозданием уже не спас ситуацию и вы считаете, что это как-то подтверждает вашу теорию?

Более того, это как раз подтверждает мои тезисы — Гоу отвратительный язык, потому что в нем значительно сложнее развивать API без поломки обратной совместимости. В C# я могу добавить метод с тем же названием, но другими параметрами. В C# я могу добавить в метод новый аргумент и добавить ему значение по-умолчанию и этим сохранить совместимость. В го такого нету, это плохой язык. Если мне необходимо добавить второй аргумент в метод — мне ПРИХОДИТСЯ ломать обратную совместимость или давать другое название метода, менее подходящее — типа dup2
То есть вы мне кинули статью, где описывается, что от Микрософта люди ушли из-за отвратительного API на более приятные технологии и шаг с значительным опозданием уже не спас ситуацию и вы считаете, что это как-то подтверждает вашу теорию?
Основной тезис статьи: каким бы ни был API — но отказ его поддержки, как правило, приводит к тому, что люди от вас уходят, да. Очень подробно и с примерами. Скажем на тот же Unix/Linux где API нельзя сказать, что уж прям верх совершенства — но он стабилен (если не касаться десктопа, увы).

При этому уходят люди не на более приятный API (на что был рассчёт у создателей C# и всей .NET экосистемы), а на более доступный.

В C# я могу добавить метод с тем же названием, но другими параметрами.
А почему вы считаете, что это хорошо?

В C++ это, в принципе, тоже возможно — однако Google рекомендует этого не делать.

Если мне необходимо добавить второй аргумент в метод — мне ПРИХОДИТСЯ ломать обратную совместимость
Да что это за танцор, блин, такой… Никому не приходится — а ему приходится…

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

Ситуация с языком Go, в общем, довольно странная: сам язык — действительно достаточно молод, но вот его разработчики — не совсем. Соотвественно хайпа в нём мало… что не мешает ему иметь сообщество разработчиков, сравнимое по размеру с такими языками, как Swift или Ruby… что достаточно удивительно, если вы вспомните, что Google не продвигает его так агрессивно, как Apple продвигает Swift и шума, подобного тому, что поднят вокруг Ruby-on-Rails тоже нету.
Соотвественно хайпа в нём мало… что не мешает ему иметь сообщество разработчиков, сравнимое по размеру с такими языками, как Swift или Ruby… что достаточно удивительно, если вы вспомните, что Google не продвигает его так агрессивно, как Apple продвигает Swift и шума, подобного тому, что поднят вокруг Ruby-on-Rails тоже нету.

Не буду говорить за всех, но лично я узнал о существовании Ruby и Ruby-on-Rails когда игрался с бесплатными системами управления проектами и наткнулся на Redmine. Про Swift слышал чуть-чуть. Зато про Go из каждого "утюга".


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


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

Сделайте клон — появится.

В нормальных системах (как хаскелевский stack, например) вместо клона можно обойтись commit hash'ем нужной ревизии.

Если какой-то проект постоянно ломает API trunk'а и заставляет использовать устаревшие версии — то от него лучше отказаться.

В проде я от него откажусь, вероятно, да. В процессе разработки — нет. В моём опыте несколько раз имело смысл взять на потестировать библиотеку, у которой не то что транк всё время ломается, а ни одного публичного релиза не было.
Для реальной разработки в go сейчас уходят от go get всех пакетов. Собираются включить dep в стандартный набор утили, когда это будет неизвестно, но его уже сейчас можно использовать. (ну либо аналоги). Все они используют как указатель версии пакета тэги в SemVer. В таком случае калечащие изменения должны выкатываться под новой major версией. А менеджер зависимостей не должен сам major обновления применять.
в go сейчас уходят от go get всех пакетов

То есть `go get` таки плохой? А почему тут жертвы его защищают?
Делаю go get и скачиваю из транка новые версии библиотек

А go get нельзя привязать к тегу?

Как раз недавно рассказывал, почему мы отказались от версионирования на фронте.


unsafePtr у шарпа и явы дженерики — это не шаблоны, а полиморфные типы.


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

Потому что создателям go "далеко за ..." и многое им видится "ненужным, излишним", после проходят годики и начинаются писаться статьи вроде Dave Chany и техническом долге go и том, что если язык назвался mainstream, то от него ждут определенных вещей: управление зависимостями, generics. Но создатели языка действуют в формате "если мы сомневаемся, то не делаем ничего и ждем".
Язык golang великолепен в большом поле задач и за год вполне можно взгрустнуть об отсутствии обобщенных типов лишь раз или два(грусть чаще накатывает от фанатов golang). Но вот станет ли язык mainstream — вопрос открытый.
По мне, несмотря на все громкие заявления о том, как будут вводиться изменения в язык (а таких заявлений была масса в 2017 году), развитие языка в 2017 провалилось: невнятный и непродуманный контекст, поломанные плагины (эпичная "бага" с тем, что они не работают нигде, кроме unix), алиасы, sync.map — это скорее недоразумения, чем развитие языка.
Но время покажет.

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

А можно, пожалуйста, подробнее для тех кто не в курсе?

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

Я не пишу на D, но пример там очень понятный, поскольку похож на любой современный язык, кроме Go

Не понял: в статье ругают Go или рекламируют D? Объясните, я просто dumbed down (из Google).
ругают Go или рекламируют D?

точно так. только по сниппетам кода понятно, что автор не дотягивает даже до туповатых, а умным себя считает только по факту того, что пишет на D (оказался умнее всех тех, кто не пишет на этом прекрасном языке)
[offtop]
минусующие (как здесь, так и в карме) могли бы и высказать в чём я не прав, аргументами меня задавить, а не так — плюнул в спину и убежал
[/offtop]

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

[offtop]
Спасибо, мил человек, никогда бы и в голову не пришло, что на Хабре есть целый профсоюз «Защиты британских троллей от оскорблений за откровенно тупые набросы с интами». Буду знать.
[/offtop]
Как можно было, например, написать утилиту:
Заголовок спойлера
package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	var err error
	reader := os.Stdin
	if len(os.Args) > 1 {
		if reader, err = os.Open(os.Args[1]); err != nil {
			panic(err)
		}
	}
	text, err := ioutil.ReadAll(reader)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(text))
}


Насчет «ада копирования». Подобные задачи (как в примере), где нельзя отделаться интерфейсами и приходится копипастить я встречал крайне редко в повседневной практике. Ну и кодогенерацию никто не отменял. В целом, конечно, печально, что нет шаблонов
C зависимостями не все так и плохо. Есть сайт gopkg, на который льют релизы библиотек, есть утилита glide, который может заморозить все библиотеки(сохранив текущие коммиты библиотек в спец файлик), да и сами google уже почти запилили менеджер dep, скоро собираются вносить в свой toolchain
И последнее заявление:
Еще одна скрипучая проблема в том, что Go — это процедурный язык (подобно тихому ужасу Си). В итоге начинаешь писать код в процедурном стиле, который чувствуется архаичным и устаревшим. Я знаю, что объектно-ориентированное программирование — это не серебряная пуля, но это было бы здорово иметь возможность абстрагировать детали в сами типы и обеспечить инкапсуляцию.

Кто же сказал, что go процедурный язык? В чем проблема обеспечить инкапсуляцию? Хоть интерфейсом, хоть просто выносом в другой пакет

Вам доводилось пользоваться кодогенерацией в своих проектах? Довольно муторное, ненадежное и небыстрое занятие. По мне, это сваливание ответственности с компилятора на пользователей, особенно в части гарантий работы.
Стоит в проекте появиться 5-10 активно используемым кодогенераторам и появляется чувство, что он обложен черными ящиками, в которых могут быть ошибки, подчас трудно отлавливаемые — разбирать сгенеренный код бывает нелегко. Вместо одного гарантированного решения задачи с обобщенными типами получается зоопарк из разных пользовательских решений, которые надо поддерживать. Это банально дорого.
Кодогенерация, на мой взгляд, это не решение, скорее затычка "вы так хотели generics — нате!"

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

И 5-10 кодогенераторов — это 5-10 скриптов, которые что-то генерируют или 5-10 типов кодогенераторов (ну там ragel, bison, flex и т.д. и т.п.)? 5-10 типов (каждый со своим языком и описанием) — это уже перебор… собственно Go потому и появился чтобы всякие бесконечные Sawzallы не генерить в бешенных количествах…

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

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

Но вы работаете с ними как с частью языка. Про это знают тесты, ide, система проверки кода, компилятор и прочее.А ещё оно неплохо интегрировано в язык. Со сторонней кодогенерацией вроде не так.

Зависит от кодогенерации (мы ведь просто о дженериках говорим). Я не настолько хорошо знаю Go, но вроде там есть какой-то инструментарий кодогенерации прямо из коробки. Понятно что, чем сложнее генерация, тем сложнее поддерживать, но вопрос инструментария — это уже частности, я просто говорил, что под капотом разницы нет, в C++ тоже кодогенерация, и программист сгенерированный код не читает, ему приходится пользоваться головой и инструментами, какие есть. Причём и там не всё радужно, шаблонный код часто совсем неподдерживаемый а сообщения об ошибках в шаблонном коде — нечитаемая портянка на весь экран. :)

в C++ тоже кодогенерация, и программист сгенерированный код не читает, ему приходится пользоваться головой и инструментами, какие есть. Причём и там не всё радужно, шаблонный код часто read-only а сообщения об ошибках в шаблонном коде — нечитаемая портянка на весь экран.

Каша из логов шаблонного кода со временем превращается в понятное (пусть и все еще большое) чтиво, сам в это когда-то не верил. Ну и вообще говоря можно получить код с инстанциированными шаблонами (e.g clang -Xclang -ast-print -fsyntax-only ...), вдобавок отладчики уже давно выводят типы

Ну, весь вопрос — сколько времени понадобится, чтобы превратилась в понятное чтиво. :) По сути это тоже перенос нагрузки на мозг разработчика. Поддержка тоже местами хромает — в CLion у меня, например, целая куча красного кода из-за С++14, если говорить об IDE.


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

Для сравнения возьмите дженерики из c#, нету там всяких танцев с бубном.
Каша из логов шаблонного кода со временем превращается в понятное (пусть и все еще большое) чтиво, сам в это когда-то не верил.

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

вдобавок отладчики уже давно выводят типы

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

В protobuf, к примеру, куда проще заглянуть в исходники посмотреть сигнатуры функций, нежели лезть в документацию

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

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

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

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

Неправда ли любопытно: кодогенерация — это рекомендуемый путь, но стоит им пойти и начинаются проблемы.
Так в прошлом проекте использовались генераторы: msgpack для aerospike, easyjson, для клонирования объектов, локальный типизированный кэш, генератор клиента на основе api. Некоторые проекты использовали большее число.
Иногда в сгенерированном коде находились ошибки и тут приходится читать этот код. Но кодогенераторы пишут кто во что горазд (в общем-то и понятно — это просто сторонние пакеты, стандарта на них нет) и разбираться в таком хозяйстве долго и дорого.
Для меня, кодогенерация — это перекладывание ответственности и гарантий с больной головы на здоровую.

Для меня, кодогенерация — это перекладывание ответственности и гарантий с больной головы на здоровую.
А какая альтернатива? Шаблоны? Ну так это то же самое, только ещё хуже. Потому что там всё точно также генерируется — только неявно. Причём в случае с кодогенерацией вы можете увидеть и понять — что происходит просто открыв сгенерированный код. А если при открытии сгенерированного файла у вас IDE сжирает несколько гиг памяти, то сразу возникает вопрос: а оно точно нам нада — то при использовании шаблонов можно заметить что «что-то пошло не так», только обнаружив, что линкеру 16GB памяти не хватает и нужно ставить 32GB только чтобы получить один бинарник на гигабайт…

Даже на C++ я часто вижу, что люди используют кодогеренрацию, а не шаблоны. Потому что как средство метапрограммирования кодогенерация зачастую — удобнее.
Дженерики как в хаскеле. Это не шаблоны в смысле плюсов и не template haskell в смысле полноценной компил-тайм-манипуляции AST, а просто реализация наблюдения, что типы данных обычно изоморфны рекурсивным кортежам. Через это можно и хеши делать, и операции сравнения, и вывод на экран, и жсон, и БД, и вообще большую часть того, что надо от кода, претендующего на манипуляции с AST и всяким таким.

А, ну а вообще полноценной системы типов хватает, когда вы можете написать функцию, которая в своей сигнатуре говорит, что она работает с любым типом, реализующим Ord, например, или вроде того. Это вообще 99% всех использований покрывает.
Кто же сказал, что go процедурный язык? В чем проблема обеспечить инкапсуляцию? Хоть интерфейсом, хоть просто выносом в другой пакет

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

Более того, пример javascript убедительно показывает, что именно инкапсуляция-то в ООП и вовсе оказалась необязательна :-)
Вы про активное использование штук типа bind?
Нет, про отсутствие приватных свойств у классов.
Incapsulation в JS используется, просто всякими Module Design Pattern'ами и прочими трюками с closure'ами.
Используется-то используется, но не требуется.

Я слонен разделять инкапсуляцию и сокрытие.

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

Самым главным в программировании я считаю это умение решать задачи. Для решения задачи есть инструменты, и язык это инструмент, в частности, go тот язык, которым быстро можно овладеть. С моей точки зрения нужно использовать инструмент, который быстрее приведёт вас к решению задачи. Кроме того есть ряд известных преимуществ языка GO, например производительность, в отличие от JAVA.
Предположим что вы решаете задачу, ту, которую до вас не решал никто, на каком языке она будет решена — неважно, важно что решена, именно это и определяет потенциал человека. Простой пример, это гений Перельман, который блестяще решил теорему Пуанкаре. В вашем же случае, умные программисты — это программисты с завышенным ЧСВ. Или как это сейчас модно, знающие лайфхаки, в языках, из разряда, как сделать чтобы коврик в туалете не уезжал из под ног. Именно этот набор знаний, вы и выдаёте за ум.


Более 8-ми лет я пишу прошивки под разные микроконтроллеры, и конечно же на си, который я знаю также, как истинные верующие отченаш. Когда нужно организовать простенький сервер, для приема и обработки данных с железки, я часто пользуюсь Go. И здесь вопроси умения возникает мало. Другое дело, когда нужно помочь с реализацией на Java, или Android. Из-за ООП, мне гораздо сложнее, тк мне необходимо продумать по сути архитектуру и структуру классов. Это даётся мне сложнее, но только по тому что это не основной мой навык.

конечно же на си

А почему не на ассемблере? Значит для Вас важен язык. Это, на мой взгляд, противоречит Вашим словам:
на каком языке она будет решена — неважно

Си — это и есть кроссжелезочный ассесблер

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

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

Можно и на ассемблере, только у каждой архитектуры свой набор команд. А это время. Если в задача — максимально короткое и эффективное время исполнения (лучше чем скомпилировал бы компилятор си), я использую ассемблер. Сейчас любой компилятор позволяет комбинировать си и асм, опять же для удобства и быстроты реализации задачи.
Время это очень ценный ресурс.
Поэтому для меня важно умение решать сложные задачи за максимально короткое время. Поэтому в Go я гораздо быстрее разберусь чем в JAVA
На С потому что для микроконтроллеров это без вариантов, если вы не хотите заниматься извращениями и нужна хорошая производительность. Не плюсы, потому что ABI нужно дописывать, он платформозависим. Asm для критически-важных (по скорости) секций, хоть это и значит, что запас минимален и кристалл выбран неправильно.
Да, можно сейчас на LUA писать «прошивки», но когда нужна прямая работа с памятью (записать такой-то бит в ячейку с таким-то адресом), или когда на практике знаешь, зачем нужны volatile-переменные, С просто удобнее.
Еще одна скрипучая проблема в том, что Go — это процедурный язык (подобно тихому ужасу Си). В итоге начинаешь писать код в процедурном стиле, который чувствуется архаичным и устаревшим. Я знаю, что объектно-ориентированное программирование — это не серебряная пуля, но это было бы здорово иметь возможность абстрагировать детали в сами типы и обеспечить инкапсуляцию.
Интересно, что и в упомянутой книге (перевод) слово «объект» встречается три раза, а слово «класс» — только два: первый раз не по делу, а второй раз:
В Go есть несколько различных типов для представления чисел. Вообще, мы
разделим числа на два различных класса: целые числа и числа с плавающей
точкой. (С.15)
Но если верить Википедии, Go — ОО язык (в отличае от Си):
Специальное ключевое слово для объявления класса в Go отсутствует, но для любого именованного типа, включая структуры и базовые типы вроде int, можно определить методы, так что в смысле ООП все такие типы являются классами.
Из дальнейшего текста вики видно, что принципы ООП понимаются в этом языке довольно своеобразно.
Из дальнейшего текста вики видно, что принципы ООП понимаются в этом языке довольно своеобразно.
Они понимаются примерно как в CLOS. Который является вполне себе ООП-языком — и гораздо более гибким, чем C++/C#/Java…
Вот взять например язык для «умных» и выразить вторую задачу:
putStrLn $ show $ foldl1 (+) [1, 2, 3.3]

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

Хорошо решим первую задачу:
import System.Environment
import Data.Char
main = getArgs >>= printFile
    where printFile (x:_) = readFile x >>= putStr
               printFile [] = getContents >>= 
                   readFile . filter (not . isControl) >>= 
                       putStr

Сколько надо затратить труда, чтобы найти это решение на Haskell? А если добавить обработку исключений? Наверно Go это другая сторона медали и вполне имеет право на существование.

Первый пример крайне просто читается, даже человеком со знанием английского (при условии, что он догадается, что str -> string и foldl -> fold).
Второе уже требует знания монад и ФП, тогда не должно являться большой проблемой.


А что касается времени на написание — то ФП, это, по моему опыту, совсем другие дебри. "Что-то накидать хоть немного работающее и сделать это быстро" — явно не про оное. Так что думаю сравнение не очень уместно. Впрочем, мой опыт может быть и не столь обширен

Ну, мой опыт с вашим не согласен. Как раз быстро накидать что-то немного работающее получается быстрее на нём.
Зачем foldl1 (+), когда есть sum, который уже давно работает поверх произвольного Foldable?

И я бы второй пример переструктурировал немножко:
main = getArgs >>= file >>= readFile >>= putStr
    where file (x:_) = pure x
          file [] = filter (not . isControl) <$> getContents
Да вариант действительно отличный. Только сколько людей могут предложить такой вариант? Сколько людей знают что такое аппликативный функтор и что монада тоже им является? А в статье написано сел и за один вечер освоил, быстро накидал и в продакшн (((
main = do
    args <- getArgs
    stdIn <- getContents
    let file = if null args
                    then filter (not . isControl) stdIn
                    else head args
    content <- readFile file
    putStr content

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

Всё дело в эстетике :) У вас вот putStr в обоих ветвях повторяется, поэтому естественный вопрос: а можно ли его как-то оттуда вынести и обобщить, например? Вынесли, теперь readFile повторяется. И так далее.

Сколько людей знают что такое аппликативный функтор и что монада тоже им является?

Любой, кто знает, что такое монада (когда я только изучал хаскель во времена ghc 6 и задолго до applicative/monad proposal, равно как и задолго до моего упарывания теоркатом, помню, сильно удивлялся, почему Monad не является субклассом Applicative). Но да, я согласен, таких людей меньше, чем программистов на Go.

А в статье написано сел и за один вечер освоил, быстро накидал и в продакшн (((

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

Ребят, а как такое тестировать?

А что именно тут тестировать?
Никак, люди так пищущие — не допускают ошибок

Ну-ну. Самоуверенные и самовлюбленные нарциссы.


Я тоже писал на Хаскеле в таком стиле, но был юн и глуп, чтобы увлечься тестированием. Как и вы.

Эх, неужели это было слишком тонко?

Ну… с учетом того, что меня заминусили, было тонко, да)

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

Реально, что там тестировать-то? :)
Go изначально разрабатывался для того, чтобы переписать тормознутые сервисы на python/php/perl, и не отстрелить себе ноги на (C/C++). Что этот язык и сделал. То, что из него стали делать вундервафлю под все задачи (как это в свое время делали из node.js) — просто хайп.

Что касается сравнения GO с другими языками, в частности Java, то Google разработала Go и начала его активно использовать, чтобы отказаться от зависимости от Oracle. Заметьте одну вещь.

  • Oracle — Java
  • Microsoft — C#
  • Google — Go
  • Facebook — php (php Hack)
  • Apple — Object-C/Swift


Просто крупные игроки не хотят зависеть от какой-то третьей стороны, поэтому делают свой «улучшенный язык», который и продвигают везде.

Сейчас Google замкнет всю свою систему на GO и станет таким же вендор локом, как и Microsoft, как Apple и прочее =)

Можно ли дополнить список Mozilla с её Rust? Кажется, новые языки создают все кто может.

У go не получилось в этом смысле — проект go-mobile скорее мертвый, чем живой, и сам Google для мобильной разработки советует Kotlin.

Устарело.
Гугл для мобильной разработки советует Dart
Ок, скажем тогда иначе, «что угодно, но не go-mobile».
Где и кому они такое советуют?

можно по подробней?

Просто крупные игроки не хотят зависеть от какой-то третьей стороны, поэтому делают свой «улучшенный язык», который и продвигают везде.

Да нет, не думаю — для Android, например, Google не стал изобретать новый язык программирования, а просто сделал свою реализацию рантайма.


К тому же, потребность в своём собственном языке никак не влияет на дизайн этого языка. Задачи Google не так сильно отличаются от задач тысяч других компаний — (микро)сервисы с максимальной утилизацией CPU и памяти. Это объясняет горутины, например. Стремление к простоте изучения и близости к C тоже понятна, учитывая тысячи работающих у них студентов, хотя для многих других компаний это свойство языка может быть совершенно ненужным или неуместным. А вот замена дженериков на кодогенерацию или использование зависимостей без чёткого версионирования лично для меня ну совсем непонятны.

использование зависимостей без чёткого версионирования
Для лучшего понимания можете глянуть, например, доклад Why Google Stores Billions of Lines of Code in a Single Repository. Там и про кодогенерацию вскользь упоминают.

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

Пожалуй так. Подчеркну, что речь не просто про монорепу, а еще про trunk based development.

Выходит, что сама концепция «версионирование зависимостей» становится лишней (в том числе для других языков, вроде Java или Python). А разработчикам Go просто не было смысла тратить много времени на ее проектирование и реализацию. Посему и сделали максимально просто (скорее даже примитивно).

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

Отлично! Как-раз искал что-то вроде С для дураков. Ещё и с книжкой сразу. Спасибо!
Если нужна книжка по Go — смотрите в сторону Кернигана (а вот его сишную лучше не читать).

Спасибо! А библиотеку для обмазывания цветом и рамочками вывода в консоль какую лучше использовать?

Если я вас правильно понял, то вы хотите ncurses для Go. Так вот, его нет. Попытки были, но когда я пробовал — всё было не оч. Если сильно хочется некомандный интерфейс, то стоит смотреть в сторону http. Серьёзно, 5 минут делов, всё необходимое уже есть в стандартной библиотеке, нет проблем с переносимостью. Если не устраивает — стоит спросить другие варианты у коллективного разума в русском Go Slack сообществе.
Спасибо! Попробую непременно оба совета.
А что не так с сишной?
Вот, к примеру, решение той же задачи на D:

вот к примеру решение той же задачи на Perl:


while (my $line = <>) {
    print $line;
}

или даже короче:


while (<>) {
   print;
}
print while(<>);

тогда уж без скобок. :)


print while <>;

Достаточно скрипта только из комментария:


#!/usr/bin/perl -p

или даже пустого perl-скрипта (дзен-вариант) :)


echo 123 | perl -pe ""
void main(string[] args)
{
    try
    {
        auto source = args.length > 1 ? File(args[1], "r") : stdin;
        auto text   = source.byLine.join.to!(string);

        writeln(text);
    }
    catch (Exception ex)
    {
        writeln(ex.msg);
    }
}

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


void main(string[] args)
{
    auto source = args.length > 1 ? File(args[1], "r") : stdin;
    source.byLine.writeln;
}



void main(string[] args)
{
    [1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln;
}

Этот код можно ещё упростить:


void main(string[] args)
{
    [1, 2, 3, 4, 5].reduce!q{ a + b }.writeln;
}

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


void main(string[] args)
{
    [1, 2, 3, 4, 5].sum.writeln;
}
В нем нет новых идей, кроме поддержки параллелизма (который, кстати, прекрасен) и это обидно.
Не так уж и прекрасен. Смотря с чем сравнивать )

а с чем?


если говорить об утилизации одного CPU и читабельности кода, то все рано или поздно приходят к тому что калбек-лапша это хелл, приходят к переключению контекста руками — yield/cede, корутины.


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


если говорить о соединении двух подходов — получаем примерно то же самое: сопрограммы/корутины (чтоб читабельно) + каналы (чтоб межпроцессно). Что Go из коробки и предоставляет.


да у Go море недостатков, но Вы говорите — есть лучшие альтернативы.
лучшие это какие?
python, perl, прочие — это все языки одного CPU. прекрасные, но одного CPU
Erlang — язык прекрасный, но порог вхождения ужасен. Как объяснить кому-то что понятия переменная нет и это хорошо — хз.
Хацкель — это уже совсем фанатам.


что еще?

А если не переключить контекст руками — он так и будет принадлежать горутине, пока она его не отдаст (не вызовет yield либо внешнюю функцию). Есть возможность зациклить горутину и потерять один тред насовсем.
Не планируется ли введение в рантайм каких то механизмов для идентификации процессов горутин с возможностью их принудительного завершения работы?
По поводу каналов — да, это типичное решение для обмена. Но мне ближе обмен сообщениями между процессами с почтовыми ящиками (заумно — модель акторов). Я не критикую каналы, просто CSP и Акторы — это разные вещи, со своими особенностями. Мне в каналах не нравится то, что канал — это дополнительная сущность; факт того, что отправляющий процесс блокируется, если на той стороне канала не читают, умерла горутина.
Да, я в курсе, что в Go это стараются решить. И я буду рад узнать о том, что решили.
Альтернативы всегда есть, но всегда можно найти причины, по которым эти альтернативы вам не подойдут.
А если не переключить контекст руками — он так и будет принадлежать горутине, пока она его не отдаст (не вызовет yield либо внешнюю функцию). Есть возможность зациклить горутину и потерять один тред насовсем.

и это же хорошо. Ну а безо всяких go пользователь на любом языке может написать while true; do nothing(); done и отстрелить тред/процесс насовсем. Но мы же обсуждаем языки, а не тех кто этими языками себе что-то отстреливает?


По поводу каналов — да, это типичное решение для обмена. Но мне ближе обмен сообщениями между процессами с почтовыми ящиками (заумно — модель акторов).

не очень я понял при чем тут акторы. я говорил о транспорте данных между процессами. при обмене данными между процессами: можно класть их в shared memory около которой mutex. Можно гнать данные через канал/пайп/сокет. Больше способов не придумали пока.
А как оно оформлено поверх транспорта — актор там или не актор — дело десятое


Мне в каналах не нравится то, что канал — это дополнительная сущность; факт того, что отправляющий процесс блокируется, если на той стороне канала не читают, умерла горутина.

дык каналы с заданным размером решат эту проблему. Или даже резиновые каналы.


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


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

задача стоит IPC между процессами. Вариантов всего два: mutex (варианты) + shared memory. Здесь в полный рост проблема стоит взаимных блокировок, производительности, сетевого масштабирования. Второй вариант — пайпы (сокеты/каналы итп). Здесь та же проблема взаимных блокировок (что Вам не нравится) + буфферизации каналов (появляется понятие "данные в пути").


есть еще какие-то альтернативы?

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

Можно вынести перезапускатор — на уровень языка или стандартной библиотеки. Вопрос только в формализации критериев.

То есть, перезапустить всё приложение целиком. Я понял, спасибо.

горутину можно перезапускать по критериям же.

Направьте, пожалуйста, где почитать.
эм, я имел ввиду что юзер может написать свою обёртку над проблемной горутиной и сам ее респавнить когда продетектит подыхание
Беда в другом: у меня есть несколько тяжелых обработок данных/внешних вызовов, которые выполняются в горутинах на каждый запрос; одна из горутин возвращает ошибку, как мне не транжирить ресурсы и прекратить выполнение тяжелых операций в других горутинах? Никак.
На этот случай есть контекст, через который можно потушить дочерние горутины. Родительская горутина, которая всех запустила узнает об ошибке и через контекст тушит остальных

Оно так не работает)
Попробуйте набросать пример — будет любопытно посмотреть.

Все верно, завершать работу, должна уметь горутина.
Попробовал набросать пример на основе документации на context — play.golang.org/p/7BHDtAgVMn
Попробуйте запустить на локальной машине и посмотреть итог (в песочнице должен быть deadlock поскольку там только один thread и он будет сразу заблокирован).
Context.Cancel может оповестить горутины о том, что пора `Done()`, но он не может прервать их выполнения. Такого механизма в go нет.

Из этого есть еще одно следствие — Golang неполноценно реализует CSP модель Хоара, поскольку в его модели над процессами для полноты модели определялся рад операций, в том числе и прекращение процесса. А golang этого не умеет и модель CSP реализует лишь частично.
Горутины являются такими легковесными благодаря тому что известны места, где может произойти смена контекста. Естественно, что прервать её выполнение в любом месте не выйдет. Можно использовать контекст и периодически проверять не отменили ли задачу.

Не совсем верно, до 1.5 вполне была возможность прерывать выполнение горутины. Да, через unsafe, но была, после ее убрали.
Время от времени проверять можно, если операцию можно разбить на отдельные части, между которыми делать проверки. Но как итог мы усложняем код на порядок.
Как правило остается только смириться с тем, что механизма для early stop и сохранения ресурсов нет и не будет.

Как правило остается только смириться с тем, что механизма для early stop и сохранения ресурсов нет и не будет.
И слава богу. В C++ мире от pthread_cancel тоже, в общем, отказались.

Не окупает оно себя. Теоретическая красота модели не компенсирует сложности, требуемые для того, чтобы её реализовать — и на практике оказывается, что пользоваться этим всё равно нельзя, так как разработчики разных библиотек не разрабатывают их так, чтобы их можно было безболезненно останавливать.
А мире C++ если количество корутин подвисших на долгое время сравняется с числом ядер машины, то тоже исполнение встанет колом?
Вопрос без шуток, любопытно, как в C++ с подобным живут.
Не с количеством ядер машины, а с количеством потоков, но да — если все они будут заняты, то всё, конец. У NGINX количество потоков рекомендуется выставлять равным числу ядер, впрочем.

Вообще же стоит задуматься над таким фактом: чуть ли не самая отказоустойчивая система всех времён и народов полностью построена вокруг кооперативной многозадачности — так же, как и «ужасное глюкало» Windows 3.x.

В чём разница? Да в том, что если вы используете нормально написанные программы (а тем «бац-бац-и-в-продакшн»), то все «ужасные» проблемы легко разруливаются аккуратным программированием.

Если же об архитектуре не думать от слова «совсем» — то и «модель Хоара» не спасёт.
В мире C++ [пока] нет корутин. pthread_* — это про потоки, настоящие, системные, а не гринтреды.

Когда в C++20 появятся корутины, надо будет смотреть, что стандарт будет требовать от шедулера.
Спасибо за грамотный ответ! Интересно будет посмотреть, как сделают гринтреды.
В мире C++ [пока] нет корутин.

что-то с Марком Лемманом и его libcoro (+libev) разве случилось?

Да нет в Coroutines TS никаких шедулеров… В каком потоке вызвали resume() — в том и продолжилась работа.

не очень я понял при чем тут акторы. я говорил о транспорте данных между процессами.
arild.github.io/csp-presentation
Слайды 10, 11, 12.

на слайде 10 справа нарисован аналог канала, ага.
на транспортном уровне все сводится к двум вариантам:


  1. shared memory + mutex
  2. pipe

а "актор" или "канал" или "lock" — это надстройки над тем или иным транспортом

есть еще какие-то альтернативы?

Distributed STM. Она все эти пайпы-шмемы прячет под капотом.

Как показывает опыт Elrang, с акторами то же бывают проблемы.


факт того, что отправляющий процесс блокируется

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

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

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

конечно

> В данном случае, это решает часть проблем связанных с тем, что писатель работает быстрее читателя.

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

мало того с какого-то момента пользователи начинают «видеть» лаг исполнения поставленных ими задач. То есть факап происходит даже еще ДО достижения мемори-лимита.

и никакой язык, а только разработчик может поправить эту проблему
и никакой язык, а только разработчик может поправить эту проблему

Только это порой выглядит как костыль. Как например mutex в Erlang'е.

Проблема ли это? В данном случае, это решает часть проблем связанных с тем, что писатель работает быстрее читателя.

смотря какое приложение конечно, в интерактивных мы вот пришли к такой парадигме:


  1. некто — источник событий — генерирует в пространство событие "у меня тут данные появились" данные прикладывает к событию
  2. события хранятся в некоем хранилище, хранение в котором сугубо временное.
  3. кому надо может подписаться на событие

от модели акторов отличается тем что


  1. в явном виде отсутствует адресат-получатель
  2. подписчиков может быть множество
  3. единый резиновый (но с fixed MAX size, fixed ttl) maildir для всех событий

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

И тут спрашивается причем тут теория взаимодействующих процессов с CSP, акторами и т.д.?

не понял я вопроса.


есть проблема (1 штука)
есть несколько ее решений:


  1. заблокировать нафиг писателя пока читатель не прочитает (Go из коробки, Erlang — с телодвижениями) — набор проблем 1
  2. не блокировать (Erlang из коробки, Go — с телодвиженияи) — набор проблем 2
  3. промежуточный (Erlang с телодвижениями, Go — с телодвижениями) — набор проблем 3

кому какой вариант нужен — тот такой и выбирает.
а теория она при каждом варианте :D

В том то и дело. Если брать языки со встроенными моделями, то везде имеем костыли и велосипеды. Поэтому говорить, что акторы было бы лучше, чем CSP как-то неправильно.
Perl 6. Высокоуровневые вещи для параллельного программирования вставлены прямо в язык.

я последние 15 лет программирую в основном только на Perl 5. Очень люблю этот язык.


смотрел на Perl 6 — крайне отрицательные ощущения: да в язык воткнули многопоточность и асинхронность, но зачем так сильно ломать обратную совместимость?
вот например регекспы: люди их пишут 100 лет одинаковые. Развивать? развивай!
но зачем скобки например тасовать причем по кругу? Добавили треугольные, ок, зачем менять смысл у других скобок? чем варианты старые не устраивали?


и так все. Perl 6 от Perl практически ничего не взял кроме какого-то ОЧЕНЬ ОБЩЕГО подхода. Поэтому хорошо бы ему не называться Perl и у него будет все хорошо.

У меня другое впечатление. Когда пишешь, по ощущениям — это старый добрый Перл. Такой же гибкий и классный. А к новому синтаксису привыкаешь.

Фанатам вот сейчас было обидно.


Конкуретность и параллельность отличная, от тупого parMap rdeepseq через Par-монаду к STM и async. А STM — божественная штука, вы просто пишете код, и он просто работает. Без дедлоков, без всего этого вот.

Использую Go последние лет 6 уже если не больше, и так привык, что практически перестал что-то писать на других языках, так немного на JavaScript еще для фронта пишу, и то уже WebAsm не за горами, думаю и его на Go можно будет компилить.
Очень сильно устраивает простота языка и еще главное независимость от кучи библиотек. Скомпилировал 1 файл, раскидал по серверам и полетело, не надо кучу зависимостей тащить и потом отлавливать конфликты или баги обновлений.

Если не используются гошные плагины ;)

Думали :) Но потом отказались, ушли в сторону микросервисов.
И правильно сделали)
* Я просто к тому, что go дает теперь не обязательно один бинарь на выходе. Эту особенность языка можно вычеркивать.
Ну под Windows он один :) Плагины там не пашут… еще :)
Плюсанул бы, но не могу. Go для тех, кто устал от тонн абстракций, сотен мегабайт библиотек. Если нужно решить какую-то задачу, ты просто её решаешь, максимально простым и быстрым путем. Код очень простой и читабельный, понимание чужого кода приходит на много быстрее, чем в других языках. В этом его прелесть. А кому краткость кода, то выбирайте Perl, не ошибетесь.
Каждый раз одни и те же тезисы в разных статьях о минусах Go, просто переформулированные.
> Не очень выразительный
Я не знаю, какие еще конструкции имел в виду автор, но в пример он привел исключения. В Go нет исключений, в FAQ есть пункт почему, нет и соответствующих кострукций. Возвращается ошибка, ее надо обработать. В C хотя бы были макросы, чтобы не копипастить, но в Go их нет. Кроме того, в Go у интерфейса error нет даже метода, который вернет код, только строковое представление. Так что если хочешь среагировать на конкретную ошибку — найди ее реализацтию и проверь type cast'ом. Или сравни текст и надейся, что не будет ложного срабатывания на другие ошибки с похожим текстом. Конечно, внутри приложения обычно все-таки используются типизированные ошибки, а не интерфейс error.
В сравнении с исключениями, где тебе достаточно знать тип исключения, на который ты хочешь среагировать, выглядит уныло. С другой стороны, ты не обязан знать, что конкретно может пойти не так, и ловить все ошибки по типу, а реагировать на фейл конкретного метода.
> Ад копирования
В Go нет типообезопасного обобщенного программирования. Когда будет — неизвестно. Копипаста, боль, страдания.
> Простой обход системы типов
Увы, если нет обобщенного программирования на этапе компиляции, оно будет на этапе исполнения. Ничего удивительного.
> Горе управления зависимостями
> нельзя указать версию
В стандартном инструменте go get нельзя указать версию, в менеджерах зависимостей glide и govendor — можно. Скоро обещают ввести стандартный менеджер зависимостей. Зависимости выкачиваются в поддиректорию /vendor для каждого пакета. Тут как раз все удобно.
> Культурный багаж из Си
> нет новых идей, кроме поддержки параллелизма
Непонятно, новых по сравнению с C или со всеми другими языками. Утиная типизация в компилируемом языке? Cтатически слинкованная стандартная библиотека и GC без зависимостей на libc размером в ~1МБ? Трансляциия в язык ассемблера целевой платформы, благодаря чему кросс-компиляция встроена в стандартную поставку? Отличий от С еще больше.
> Простота для собственной выгоды
Мы тут горшки обжигаем. После кода на Go, приятно сесть и написать, например, немного обощенного кода на C++. Но потом, возможно, его перепишут другие люди, и не раз. Которые тоже горшки обжигают, и у них дедлайны, и голова болит, и дома надо быть в 8. И мне придется его отлаживать.
В Go есть исключения в виде defer-panic-recover.
Но сахара для обработки ошибок кот наплакал и в результате на одну строку полезного кода приходится три шаблонного.

А разве в первом примерно когда идет считывание файла, не происходит ли там передача потока в другую goroutine? Сам go ещё не знаю, но насколько мне представляется схожесть с nodejs, во время считывания файла, и ожидания ответа от HDD, возможно фоновое выполнение другой подзадачи. И в таком случае, go сильно выигрывает у D и у C, т.к. передачу контекста там реализовывать сложнее чем в го.

Нет. Пример абсолютно однопоточный.
Go с Javascript (и нодой) сравнивать по языковым конструкциям некорректно, поскольку в JS многопоточность не подразумевалась, и авторам пришлось совать в язык неявные async-костыли, чтобы не вешать нарисованный в том же потоке интерфейс. А у Go многопоточность самая обычная — ее нет, если явно в коде не попросить.
(Ну да, если придираться, то системных потоков там несколько, но у программиста к управлению ими доступа почти нет, и с точки зрения программы логический поток у нее один)
Не совсем, ключевые вещи из стандартной библиотеки будут выполняться многопоточно и без вмешательства пользователя. Каждый запрос, приходящий в 'http/net' (HandleFunc, ListenAndServe).
Хм если появились статьи почему Go плох, значит уже стоит обратить на него внимание.
  • Если молоко прокисло, значит надо его пить
  • Если появились статьи о вреде курения, надо купить сигарет
  • Если появились статьи почему Go плох, значит уже стоит обратить на него внимание
  • Если девушка умерла, самое время сделать ей предложение
PHP плохой язык, на нем пишут кучу народа.
JavaScript плохой, ситуация повторяется.
C/C++ плохой ситуация повторяется.
Это какая то закономерность.
PS. Я не отрицаю что у всего вышеперечисленного есть недостатки…
Go язык утилит и микросервисов. Он не плох. Просто в мире утилит и микросервисов ему очень быстро стало тесно, а авторы так далеко его развитие не планировали.
Спорно, изначально язык позиционировался, как mainstream язык общего назначения.
Javascript тоже изначально позиционировался, а вышло вон оно как.

Справедливости ради, юзеры просят не многого, но очевидного, без чего mainstream язык обречен:
— Generics
— Управление зависимостями
— Более настойчивая система ошибок
Да, об этом и Дейв Чейни пишет, разве что упоминает еще и immutability. Что в общем-то требования не заоблачные, но надо понимать, что язык не соответствующим ожиданиям для mainstream сейчас, может оказаться обреченным для входа во многие области — просто потому, что программисты ожидают минимального набора фич для современного языка.
Удручает, что движения в этих направлениях незаметно. Ну разве что dep пилят, но предварительное знакомство не обрадовало.
А что не так с godep, кроме нестандартного формата в сравнение с Glide?

Что лично я наблюдал где-то месяц назад: медленный (https://github.com/golang/dep/blob/master/docs/FAQ.md#why-is-dep-slow), клонирует по новой зависимости, если в них есть обновления. Если добавят кэширование, то будет приличнее.
Второе — сырой, иногда валится без причины.

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

Прошло много лет, и миллионы обезьянок действительно прыгают и стараются — одни код пишут, другие код ломают, третьи делают инструментарий — красота!
Похоже не просто микросервисов, а инфраструктурных микросервисов. Писать на нём микросервис начисления процентов по счетам клиентов со множеством стратегий рискованно, по-моему в перспективе лет на 5-10, если стратегии появляются и изменяются не реже раза в месяц.
«Значит хорошие сапоги, надо брать» (с)
Странный вывод для такой бурной критики:
Его просто читать и просто использовать.

Разве не это главное для синтаксиса?

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

Кмк это всегда баланс между читаемостью и выразительностью.
С одной стороны я хочу как можно лучше выразить идею в коде, с другой — чтобы язык не позволял вливать в код все оттенки личного чувства прекрасного каждого программиста (иначе задолбаешься разбираться в чужом коде или кто-то — в твоем).
Этот баланс конечно тоже лично у каждого свой (если конечно о нем задумаешься), но в Go он уж слишком смещен в топорность «читаемость».
По личному опыту нужно минимум три месяца fulltime разработки, чтобы вникнуть в язык и начать в нем разбираться, а тут типичное «я пишу на языке XXX, а Go не работает так, как XXX. Значит Go плохой/кривой».
Это прям как лакмусовая бумажка.
Те, кто с языком работал основательнее, резонно замечают, что есть только проблема с дженериками, остальное — сделано довольно хорошо.

Я с Go не работал основательно, так что стало интересно: какое откровение за N месяцев фуллтайм-разработки на Go должно на меня снизойти, чтобы я понял, что обработка ошибок в Go сделана правильно?

Это откровение называется «стокгольмский синдром»

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

Лол. У него проблем выше крыши. Вот недавно в комментарии описывал, что в нем совершенно уродские теги/аннотации/декораторы, словно их придумывал человек с сильным отставанием в развитии. Это ведь не относится к Дженерикам? ;)
Кстати, если человек говорит, что в Go нет инноваций — он Go учил два дня и языка не знает.
Интерфейсы в Go — вот главная инновация, я бы сказал козырь перед другими языками.

Выгллядит как haskell's typeclass или abstract class в C++, interface в Java/Delphi/PHP/остальных ООП языках

Серьёзно? В каких из этих языков реализация интерфейса пишется независимо от самого интерфейса и, в принципе, может быть «залита» в VCS до того, как туда попадёт описание самого интерфейса?

О том, что GNU C++ signatures (которые и явились прототипом интерфейсов в Go) «срисованы» с Haskell type classes — сами разработчики говорят. Но вот «abstract class в C++, interface в Java/Delphi/PHP/остальных ООП языках» — это совсем о другом.
Серьёзно? В каких из этих языков реализация интерфейса пишется независимо от самого интерфейса и, в принципе, может быть «залита» в VCS до того, как туда попадёт описание самого интерфейса?

Ну, вот с Вики список: Prolog, D, Perl, Smalltalk, Python, Objective-C, Ruby, JavaScript, TypeScript, Groovy, ColdFusion, Boo, Lua, Go.


И это, скорее всего, далеко не все. И компилируемые среди них таки есть.


Это называется утиная типизация и в этом тоже нет ничего нового.

Я и не говорил, что это что-то суперновое. Но в тех вот языках, о которых автор перевода говорит (C++/Delphi/Java/PHP) — этого нет. Есть только в «остальных ООП» языках, но в «остальных ООП», есть, как бы, вообще всё…
Я бы этот момент рассматривал как небольшую странность а не как ключевую особенность.

Ну вот ни разу не было необходимости писать реализацию интерфейса раньше его самого…
Я бы этот момент рассматривал как небольшую странность а не как ключевую особенность.
Если это не «ключевую особенность», а «небольшая странность», то почему вокруг добавления подобного подхода в C++ сломано столько копий?

Ну вот ни разу не было необходимости писать реализацию интерфейса раньше его самого…
Вопрос не в написании реализации интерфейса раньше описания интерфейса, а во вполне эквивалетной этому возможности по реализации интерфейса, на описание которого вы, при написании вашего класса, вообще не смотрели. Какие-нибудь интерфейсы типа BasicLockable или Swappable, как правило, оказываются реализованы без явного намерения их реализовать.
А как вы поймете, что lock и unlock связаны с тредами, а не совершенно другая логика класса Gate?
А как вы поймете, что lock и unlock связаны с тредами, а не совершенно другая логика класса Gate?
Никак — и в этом весь смысл. Совершенно неважно: захватывает lock какой-нибудь mutex или тыкву. Важно, что lock — захватывает ресурс, а unlock — его освобождает. Для класса, которому нужен BasicLocable — этого достаточно.
Почему вы думаете, что lock — захватывает, а не замыкает дверь? Ну в том плане, что у него бизнес-логика совершенно из другой области, никак не связанная с тредами. И unlock срабатывает только если у пользователя есть права доступа к двери.
Ну я всё-таки исхожу из идеи что программу пишет не макака. И если произошла подобная коллизия, то передавать обьект туда, где он не уместен разработчик не будет.

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

Так вот описывать интерфейсы там, где они потребляются и не заставлять из описывать там, где они реализуются — просто удобно.
Вам удобнее сам факт того, что вы не пишете implements BasicLocable и в этом ваше удобство?
То что я не пишу implements BasicLocable — это фигня. Главное — что я при этом не вытаскиваю и не включаю в проект модуль, где этот BasicLocable описан.

То есть я могу поддержать все интерфейсы требуемые MySQL'ем, PostgreSQL'ем и, скажем, Oracle'ом одновременно — и не втягивая в проект библиотек MySQL'я, PostgreSQL'я и Oracle.

Причём для этого мне не нужно «городить огород» с генераторами фабрик, как в Java, а несоотвествие типов таки будет выявлено в момент сборки, а не в рантайме.
Скажите, а как вы будете свою поддержку MySQL, PostgreSQL и Oracle тестировать не втягивая в проект их модули?
Так же, как в ядре любой операционки тестируется поддежка железа, которого на столе у разработчика нет.

Там и тогда, где это можно использовать — там можно и протестировать.

Вы не поверите, но в природе существуют ещё программисты, способные написать код так, что его не требуется перетестировать по 100 раз после простейших изменений.
То что я не пишу implements BasicLocable — это фигня. Главное — что я при этом не вытаскиваю и не включаю в проект модуль, где этот BasicLocable описан.

Это скорее вопрос корректного разбиения на модули. Так-то ничего страшного во включении хоть сотни «заголовочных» модулей нет кмк.
Пример с разными СУБД — немного некорректен на мой взгляд — это все же иные интерфейсы, специфичные для каждой БД, а выше речь шла о неких общих, без специализации. Т.е. применимо к БД это будет некий интерфейс доступа к БД, на который будут опираться как ваша программа, так и коннекторы БД.

В соседнем треде Rust обсуждали, я там пример придумал про тайпклассы и концепты, а тут он пригодился, хорошо-то как! В общем, как вы отличите Forward iterator от Input iterator? Как вы на уровне концептов гарантируете, что он многопроходный?

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

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

Модуль использующий интерфейс обязан про него знать (иначе, как бы, непонятно как его использовать). Модуль
предоставляющий интерфейс — не обязан (программисту про него знать, в общем, полезно — но тоже не всегда обязательно).
Хорошо, но ведь уменьшение количества зависимостей — это не самоцель, не так ли?

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

Но в больших компаниях принято на каждый новый компонент полочать «добро» от лигала (и не только). То есть если очередная версия захочет вытащить клиента MS SQL только для того, чтобы реализовать описанный в этом клиенте интерфейс — то вам придётся получать добро на добавление в ваш репозиторий этого самого клиента MS SQL!

Что может растянуться на недели и месяцы.

Погодите. Вы сейчас говорите о прямой зависимости или о транзитивной?

С прямой все понятно — если нужно реализовать интерфейс для MS SQL — значит, надо добавлять зависимость от их клиента. Это нужно даже в go, потому что во всех случаях кроме самых тривиальных понадобится описание структур данных.

Что же до транзитивной — то тут вовсе не обязательно выкачивать все подобные зависимости. В тех же Java или C# если вы не пользуетесь теми классами которые реализуют интерфейс MS SQL — то и описание интерфейса тоже не нужно!
С прямой все понятно — если нужно реализовать интерфейс для MS SQL — значит, надо добавлять зависимость от их клиента.

Нарушает DIP в целом. MS должна предоставить отдельный пакет с интерфейсом и отдельный пакет с реализацией этого интерфейса. Эх, где этот идеальный мир...

DIP следует применять к коду а не к пакетам…

Почему к пакетам не следует применять?

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

Я бы ещё побеседовал с лигалом о том, является реализация тайпкласса из другой библиотеки использованием этого тайпкласса, даже если вы не делаете import The.Great.DB.Bindings.
По уму, у forward iterator должен быть конструктор копирования, а у input iterator — только перемещения. Тот факт, что input iterator, формально, требует наличия конструктора копирования, объясняется историческими причинами — input iterator появился раньше, чем move semantics
По хорошему-то нужно делать то, что отлично делает perl и хорошо делал python (пока они там все белены не обьелись) — что-нибудь типа

using C++20;

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

Тогда от них можно было бы отказываться постепенно…

А так — каждый новый стандарт делает вид, что он существует «в вакууме», старых программ «типа нет» — но при этом мы всё время о них думаем… Шизофрения какая-то…
А флаги компилятора вам чем не угодили? То же самое ведь практически — единственное, нельзя их на часть файла включить, только на весь.
Флаги компилятора не угодили тем, что они не являются частью стандарта.

Тем самым они не дают возможности постепенно убирать из него фичи: вначале делаем возможность фичу выключить, потом делаем возможность её включить, но выключаем по умолчанию, потом — отказываемся от неё совсем.
Честно говоря, не понял, зачем флагам быть частью стандарта. Флаги вроде "-std=c++17" делают ровно то же самое, что и гипотетический
using C++17;
в начале файла.
Флаги всего лишь включают режим поддержки соответствующего стандарта. Сам же исходник ровно никак не указывает на то, какой стандарт в нём используется. Потому в новых версиях стандарта нельзя убирать возможности — изменение опций компилятора (которое в подавляющем большинстве систем сборки не делается на уровне одного файла — только на уровне одной библиотеки, а иногда и на уровне одного проекта) не должно приводить к тому, что код вдруг перестанет компилироваться.
Как завезут модули, так что-то такое даже может иметь смысл в контексте плюсов, да.
Какие-нибудь интерфейсы типа BasicLockable или Swappable, как правило, оказываются реализованы без явного намерения их реализовать.

И это сильно не всегда хорошо.

Увы, но разные разработчики зачастую выбирают разные имена для одних и тех же методов. После чего все идеи насчет «интерфейсы оказываются реализованы без явного намерения их реализовать» ломаются.
Кстати, да, тоже интересное замечание, в каком-то смысле обратное моим претензиям к такому утиному подходу.
Это вы про замечательную возможность принять цветной снег за апельсиновое мороженое, потому что он холодный и желтый?

Если нам нужно холодное и жёлтое, то какая разница мороженое это или снег? :)

Ну, дело вкуса.
Можно повернуть вопрос и по другому — а в каких языках отказались от любых вариаций множественного наследования? И тогда задуматься — а может отказались с какой-то целью?

Хотя, справедливости ради, мне рыдать хочется что теперь реализации методов по-умолчанию привносят в интерфейсы C# :( в Java они уже давно имеются.
В каких из этих языков реализация интерфейса пишется независимо от самого интерфейса и, в принципе, может быть «залита» в VCS до того, как туда попадёт описание самого интерфейса?

А оно надо? Я бы предпочёл, чтобы компилятор имел возможность проверить, всё ли я определил и тайпчекается ли оно, в точке определения реализации интерфейса.


А вообще это сурово пахнет концептами C++, а соответствующий пропозал появился до релиза Go, ЕМНИП.


В гошных тайпклассах есть, кстати, multiparam type classes? Fundeps?

А оно надо? Я бы предпочёл, чтобы компилятор имел возможность проверить, всё ли я определил и тайпчекается ли оно, в точке определения реализации интерфейса.
Ну значит вам больше понравятся другие языки. У обоих подходов есть плюсы и минусы.

А вообще это сурово пахнет концептами C++, а соответствующий пропозал появился до релиза Go, ЕМНИП.
Я бы сказал, что Go'шный ООП очень сильно пахнет GNU C++ сигнатурами, которым, если бы они были людьми, уже можно было пить и курить — это ж почти четверь века назад было всё релизовано! Не исключу, что и раньше, чем в Haskell подобные вещи пробрались, но точно не скажу — это нужно целое расследование проводить…

И да — некая схожесть с концептами и type class'ами наблюдается, но в целом — это не совсем то.

В гошных тайпклассах есть, кстати, multiparam type classes? Fundeps?
Вроде бы нет, но на 100% не уверен.
Ну значит вам больше понравятся другие языки. У обоих подходов есть плюсы и минусы.

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

Не исключу, что и раньше, чем в Haskell подобные вещи пробрались, но точно не скажу — это нужно целое расследование проводить…

Придуманы они были не позже 89-го года, вот в этом папире (вообще забавное чтиво с высоты почти тридцати лет). Судя по ссылкам на странице с описанием сигнатур, они появились году в 95-м.

Но это я так, из любви к искусству.
Судя по ссылкам на странице с описанием сигнатур, они появились году в 95-м.
Нет, точно раньше. В GCC 2.6.3 (самая старая версия