Как стать автором
Обновить

Комментарии 369

Вторая программа выводит на экран:
2009/11/10 23:00:00 Working with a=47, b=0

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

В сущности, мы изменили сигнатуру функции,

Но ведь сигнатура функции не поменялась, она как принимала на вход структуру Params, так и принимает

По-моему претензия в том что параметры у функции по-умолчанию обязательны, а поля структуры — нет и инициализируются значением по-умолчанию. Это, с точки зрения автора, говорит об ошибке проектирования языка.

Да, добавлю лишь то, что в Rust вы не можете инициализировать структуру, которая не реализует trait Default, не указав все поля, которые есть. И даже если реализует этот trait, в инициализаторе явно надо писать ..Default::default() вроде, показывая что остальные поля будут инициализированы значениями по умолчанию

Зато когда вы напишите ..Default::default() вам откроется уникальная возможность добавить новое поле, забыв его проинициализировать

Не совсем так - это будет ровно такое же поведение что и в Go, т.е. инициализация значением по умолчанию, но это явно и вы для этого должны приложить усилия (либо руками реализуя Default либо через #[derive(Default)]), в отличии от неявного поведения, которое никак не выражено синтаксически в вашем коде.

Совершенно согласен с утверждением, что явное лучше не явного
Иметь возможность обязательно инициировать все поля и иметь возможность явно прописать когда это не нужно - очень хорошо
Но лично я - буду за именно обратный путь, меня устроит если появится возможность объявлять структуры так, чтобы при их инициализации нужно было бы инициализировать все поля
Это именно путь когда инструменты языка сужают возможности, а не расширяют
Имхо такой путь прозрачней
Правда кто я такой чтобы моё мнение кого-то волновало ))

меня устроит если появится возможность объявлять структуры так, чтобы при их инициализации нужно было бы инициализировать все поля

Подождите, в Rust же именно так и есть? или я вас не понял? Как раз в нем если ничего не указывать явно (с помощью derive), не реализовывать Default trait, будет именно такое поведение

У меня была нужда проверять автоматически, что структура заполнена. Это были структуры со словом Config в названии. Для этого можно легко настроить линтер. Работает отлично и спасало

Эта штука в Go напоминает проблему null'ов, по какой-то причуде названной фичей и расширенной. Всё-таки лучше, когда возможность отсутствующих значений явно прописана ввиде Option<Value or None> вместо того чтобы ожидать их везде.

Ну в этом ведь и состоит дао Го (явно унаследованное от юниксового KISS): не надо тратить лишнее время чтобы настучать это самое ..Default::default() (горизонтальное и два вертикальных двоеточия, ух), а потом ещё ходить по декларациям и проверять, где оно прописано. Все структуры ведут себя в этом аспекте одинаково. Простота как она есть.

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

зачем вам знать количество полей? Просто заполняйте те, которые нужны или используйте "конструктор"

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

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

import std.stdio, std.traits, std.format;

struct Params {
	mixin Strict;
	int a;
	int b;
}

void work( Params p ) {
	writeln( "Working with ", p );
}

void main() {
	work( Params(47) );
	/+
		Error: none of the overloads of `this` are callable using argument types `(int)`
			Candidate is: `onlineapp.Params.Strict!().this(int _param_0, int _param_1)`
	+/
}


mixin template Strict() {

	@disable this();

	this( FieldTypeTuple!(typeof(this)) args ) {
		static foreach( Index, Field; FieldNameTuple!(typeof(this)) ) {
			mixin( format( q{ this.%s = args[ %s ]; }, Field, Index ) );
		}
	}

}

Это D?

Агась.

В Go можно использовать обёртку Optional, если уж прям очень хочется для примитивов понимание "был параметр явно присвоен или нет: https://pkg.go.dev/github.com/searKing/golang@v1.1.18/go/util/optional

Правда есть минус - теперь мы "потеряли" тип примитива и должны его знать

import "github.com/searKing/golang/go/util/optional"

type Params struct {   
  a optional.Optional   
  b optional.Optional
}

func work(p Params) {   
  log.Printf("Working with a=%d, b=%d", p.a.Get(), p.b.Get())
  if p.a.IsPresent() {
    log.Printf("calc: %d", 1+p.a.Get().(int))
  }
  if p.b.IsPresent() {
    log.Printf("calc: %d", 1+p.b.Get().(int))
  }
}
func main() {
  work(
    Params{
      b: optional.Of(2),
    },
  )
}

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

Не говоря о НЕтипобеопасности

ну, можно и так - тут потери типа уже нет. Правда теперь в лог пишется адрес, а не значение поля. Но тут либо шашечки (примитивы с default value), либо ехать

type Params struct {
	a *int
	b *int
}

func work(p Params) {
	log.Printf("params: %++v", p)
  log.Printf("calc: %d", 1 + *p.a)
}

func main() {
	a := 2
	
	p := Params{a: &a}
}

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

когда примитив можно явно не указывать и ему присваивается default value

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

Далеко не всегда default value типа имеет какой-либо смысл в реальном мире в том контексте, в котором этот тип используется в структуре. Хотя ладно: когда его нет, это ещё ничего, хуже, когда это значение по умолчанию смысл реально имеет - и потом поди догадайся, забыл автор его инициализировать или там и правда этот нолик стоит и с ним нужно работать (и, например, дебажить, почему у вас где-то косяк в коде дальше - проблема в вашей логике или этот нолик забыли инициализировать, что привело к неконсистентному состоянию данных в этой структуре).

У нас есть проект, где бэк на Go, где вот так приходит факт, что поле author пустое у объекта
{ title: "", author: { id: "", firstName: "", lastName: "" } }
Или enum'ы приходят так { someEnum: "XXX_UNKNOWN", } опять же потому что "XXX_UNKNOWN" — это дефолтное значение.
Очень часто в данных есть огромная разница между 0 и Some(0).

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

да

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

Да. Честно, я не понимаю, зачем эту "фичу" вообще реализовали, она даже больше кода в компиляторе требует.

Претензия высосана из пальца, так как го выдаёт ошибку если не все поля инициализированы в структуре, но он сам указал, чтобы остальные поля были default, указав ключ-значение.

Этот код не скомпилируется:

package main

import "log"

type Params struct {
	a int32
	b int32
}

func work(p Params) {
	log.Printf("Working with a=%v, b=%v", p.a, p.b)
}

func main() {
	work(Params{
		47,
  }) // <- Error: too few values in struct literal
}

Так ещё лучше: получается, что, чтобы Go выдал ошибку, нам нужно не учитывать имена полей, а вместо этого держать в голове их порядок?

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

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

Можно указывать, но можно и не указывать. А если указали, то не обессудьте - вот тут для вашего удобства у нас такое поведение дополнительно зашито, читайте справочник по нашему самому простому языку в мире, стр 1531, третий абзац снизу. Я бы ещё понял, если бы дефолтное значение задавалось явно, как kwargs в питоне:

type Params struct {
	a int32
	b int32 = 0
}

Не надо держать. IDE подсветит.

В первом случае я указал явно field `a` со значением, тогда b будет иметь значение `0` по умолчани.

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

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

Мне например проще управляться с указателями, чем понять код где через слово self-self-self или this-this-this.

Потому что он топит за Раст, очевидно же, хотя это и языки с совершенно разными применениями и скоупом. Для 2022 года довольно типичный rant. Сейчас вот смотрю плашку справа, Go 122 вакансии, Rust 21 -- не даёт покоя это любителям раста аж до скрежета зубовного, кушать не могут.

Нет, вы это сами себе придумали. :)

Не пишу на Раст. Не топлю за переход на один конкретный язык. Это не мешает мне утверждать, что Go - пример отвратительного языкового дизайна, и для того, чтобы стало лучше, чем с ним (если вы конечно не фанатик), достаточно сменить язык на любой другой популярный, подходящий по предметной области. В фронте есть тайпскрипт, в бэке - что угодно от C++ (там конечно своих can of worms хватает), языков с явным рантаймом вроде Java/Kotlin/Scala и C#/F#, и до вещей для любителей экзоткики вроде Haskell.

Amos? Не узнаю в гриме.

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

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

Автор статьи Amos Wenger -- поэтому мне непонятны слова "Нет, вы это сами себе придумали. :)" от кого-то, кроме него. Только он может это сказать.

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

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

Потому как даже среди тех, кто статью всячески ругал и защищал го в комментариях HN, реддита, и куда ещё там его статьи шерились, такого нелепого мнения я не встречал.

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

Я уже написал всё, что хотел -- я считаю, что автор под критикой Го прикрывает продвижение Раста. Sapienti sat, dixi.

P.S. В обсуждении на HN упоминаний Раста 111 штук: https://news.ycombinator.com/item?id=31205072 Без комментариев.

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

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

Мне кажется он просто боится клешни отрастить и начать питаться падалью из-за частого упоминания Rust. Хорошо что языки программирования на самом деле не заразные.

Прям таки и большой выбор языков для бэка... Тащить java и производные либо отдаться мелкомягким? Просто отличный выбор :)

А остальные языки уже не компилируются что ли?

Для бэка довольно много языков по факту. Если опираться на аргумент "у Go такая привлекательная асинхронная среда выполнения и сборщик мусора, что они компенсируют все остальное", то можно в качестве альтернативы взять упомянутый в статье Elixir, у которого и асинхронная среда выполнения и сборщик мусора сильно лучше, чем у Go. К тому же, при необходимости в него можно добавить щепотку Rust, если вам где-то в проекте нужна числодробилка.

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

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

Ближе всего к мейнстриму эта штука: https://vibed.org/

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

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

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

Под "зачахла" я подразумеваю, что активность коммитов существенно снизилась, см. график по ссылке, продублирую вам её: https://github.com/vibe-d/vibe.d/graphs/contributors

Ну, и вы всерьёз считаете активным проект, у которого между версиями 0.9.4 и 0.9.5 проходит примерно год? А 384 issues намекают, что стабильность весьма условна. На половину из них даже никакого ответа нет, что на практике, скорее всего, значит что нет ни одного человека, который занимается поддержкой этого проекта за зарплату.

Не ошибся. Это ветка "что бы посоветовать человеку вместо го". Не будьте таким эгоцентричным.

Ну и гадать по графикам вы не умеете. До середины 2018 шла активная работа над двумя мажорными версиями 0.7 и 0.8. В середине 2018 развитие седьмой версии было завершено.

Проект над которым продолжают работу - активен. Зрелому проекту частые релизы ни к чему. Всего 384 issues только 16 из которых - баги, что весьма не плохой результат. Ну а на зарплате там никто не сидит, да.

Блин, чувак, пиши себе на D, никто ж тебе не запрещает.

Но нафига ты влез в ветку сравнения Elixir c Haskell, которые оба уже много лет имеют веб-фреймворки, до которых vibe.d как до луны что по фичам, что по стабильности (88 issues у Yesod и 26 у Phoenix). А ты пытаешься нас убедить, что 384 - это мало. Нет, это over дохера для веб-фреймворка. Ладно бы это только цифра была, ты зайди на Github, посмотри, что по большинству issues никакого обсуждения нет, им даже не присвоены никакие метки. С таким подходом к разработке кол-во issues будет только расти год от года и сколько там среди них багов никто не знает, потому что их никто тупо даже не читает. Что это, если не заброшенность?

Зрелому проекту частые релизы ни к чему.

Зрелому то да, только vibe.d до зрелости пока не добрался. Где пруфы, что авторы принципиально не планируют версию 1.0? На их сайте я такого тезиса не увидел. Или вы просто выдаёте желаемое за действительное?

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

Овердохера - это 5к ишьюсов у GHC против 3к у DMD. И что? Да ничего. Тоже мне гадание по гитхабу.

Во-первых, некорректно с темы фреймворков на тему самих языков перепрыгивать.

А во-вторых, с каждым коментом ты закапываешь D всё глубже и глубже. Я интереса ради зашёл в их багзиллу 🤦🏻‍♂️
И что я вижу: 4719 issues found. Видимо, с тех пор как ты заходил в issues tracker своего любимого ЯП, он успел ещё почти 2k issues набрать.

И чего ты сравниваешь с GHC, который себя как исследовательский проект позиционирует? Или ты посмотрел на Elixir с его 26 issues и обосрался от зависти? xD
Можешь даже 249 issues от Erlang/OTP приплюсовать.
А то вы со своим D даже хипстерский Crystal в 4 раза по кол-ву issues обогнали.

Просто у Дмитрия склонность форсить проекты, которые "не взлетели". К слову сказать, действительно интересные, классные технологии. Но, к сожалению, нужные 2,5 энтузиастам :)

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

Прошу меня извинить, что я влез в ветку сравнения Elixir c Haskell.

Ну а какой смысл рассказывать людям про проекты, которые "взлетели"? Они и так про них знают. Каждый раз, когда я вижу очередное нытьё про Go или C++, React или Angular, мне становится грустно от того, сколько времени люди убивают на бестолковые решения, вместо того, чтобы вложить его с большей пользой для "взлёта" чего-то, что может вытянуть индустрию из этого болота.

А потом ты понимаешь, что болото — оно не следствие каких-то плохих языков, а следствие более социально ориентированных вопросов — как люди выбирают, что делать, как делать, и с кем делать.

В этом я могу вас понять. Но D, к сожалению, закопали сами разработчики, сначала расколов и так небольшое комьюнити на Phobos и Tango прям аккурат в момент релиза 1.0. А потом и полгода не прошло, как бросились вторую мажорную версию языка пилить. В довершение ко всему этому ещё и не уловили тренд на OpenSource. Тем самым отложив интеграцию с gcc лет на 10.

А программисту хочется хотя бы иметь ощущение, что авторы ЯП понимают, что они делают, и придерживаются хоть какой-то генеральной линии. Возможно, в этом и причина популярности Go. Да, генеральная линия его разработчиков - треш и угар, но они неукоснительно её придерживались, как минимум, 8 лет.

Да-да, у го прекрасная интеграция с гцц, а дженерики не раскалывают сообщество. Всё, что вы перечислили, на популярность не влияет вообще.

Да-да, у го прекрасная интеграция с гцц

Тем не менее, через 3 месяца после выхода Go 1.0 она уже была. А сколько лет с релиза D 1.0 прошло до включения в GCC?

а дженерики не раскалывают сообщество

Потенциально раскалывают. Но языку уже 10 лет, теперь он может себе позволить хоть несовместимую версию 2.0 выпустить, это уже не сильно заафектит его популярность.

Всё, что вы перечислили, на популярность не влияет вообще.

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

А чего Cloud Haskell до стабильной версии не дотащили и забросили?
Не нравится мне shared memory, даже под соусом STM.

Так то мне многие идеи Haskell импонируют, но OTP - это прям самое мощное и самое подходящее, что я видел, для веб-разработки среди порядка 20 ЯП.

Под BEAM, кстати, появился ML-подобный язык, в активной разработке сейчас: Gleam. Как автор признаётся, он хотел его сделать похожим на Haskell, но из-за негативной обратной связи отказался от этой затеи.

Спросил у людей, плотно пиливших cloud haskell — вы не поверите, но забросили именно из-за отсутствия интереса у коммьюнити в пользу (D)STM.

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

Мимопроходил - а Akka Typed в скале - это не те самые строго типизированные акторы?

Да, они. Но к JVM у меня какая-то личная неприязнь. Всё, что я на ней видел, имело медленный старт и дико жрало память.

Возможно, в Akka.NET тоже что-то подобное есть, но до F# я пока не добрался)

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


Но это, наверно, на уровне самого языка надо делать, т.к. объём работ большой и авторы отдельной библиотеки на одном энтузиазме далеко не уедут.

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


Я вообще за минимальный и тупой язык, и перенос максимального количества всего в библиотеки. Условный row polymorphism так, конечно, не сделаешь, а вот акторы эти — ИМХО вполнле.

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

Я вообще за минимальный и тупой язык

Как же вы с Haskell уживаетесь? Я бы не назвал его ни минималистичным, ни тупым.

Лично мне распределённый STM больше понравился. 

По-моему, DSTM, как и CloudHaskell, тоже давно заброшен.

Как же вы с Haskell уживаетесь? Я бы не назвал его ни минималистичным, ни тупым.

Довольно паршиво, Haskell is the new C++.


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

А можно несколько примеров этих альтернатив?

Idris, например. С «несколько» уже сложнее.

Слышал, что Idris "исправляет" какие-то моменты в хаскеле, но он ещё и минималистичный и расширяемый?.. А есть шансы, что он начнёт теснить хаскель? Или преимущества не настолько большие, чтобы перевесить отсутствие библиотек и инфраструктуры?


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

Слышал, что Idris "исправляет" какие-то моменты в хаскеле

Да, там завтипы сразу правильно сделаны, без нагромождения всяких GADTs, type families и прочего. А во втором идрисе довольно занятные линейные типы, к тому же.


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


но он ещё и минималистичный

Да, нет десятков расширений, как в GHC'шном хаскеле.


и расширяемый?..

Ну это пока что — компилятор мелкий :]


А есть шансы, что он начнёт теснить хаскель? Или преимущества не настолько большие, чтобы перевесить отсутствие библиотек и инфраструктуры?

Зависит от того, как будет эволюционировать хаскель. Если туда прям завтра смержат готовую реализацию условного -XDependent, то шансов мало. Если нет — то побольшое.


Но перевешивать отсутствие библиотек и инфраструктуры действительно тяжело.

Новый ЯП не хотите сделать (без прикола)? Чтобы простой ЯП + библиотеки из Haskell/Scala/F#/etc?

Не получится.


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

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

Комунити у хаскела шибко токсичное в собственной илитарности

Я бы сказал, что это либо про коммьюнити из этак 2005-го, либо про людей, которые пишут очередной туториал по монадам. Коммьюнити что в IRC, что на условном SO вполне отзывчивое и не то чтобы илитарное.


гайдов/доков не сказать чтобы достаточно

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


Когда я что-то делал более мейнстримное, какой-то там веб-сервер, или доступ к БД — к этому гайды вполне находились.


Плюс батарейки в комплекте вроде не очень вкусные

Prelude, с одной стороны, действительно стрёмный, с другой — дописываешь пакеты с hackage в package.yaml и вперёд, а их там дофига, включая вкусные :]

Я бы сказал, что это либо про коммьюнити из этак 2005

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

Prelude, с одной стороны, действительно стрёмный

опять же я не столько за стандартные библиотеки, сколько за УДОБНЫЕ инструменты каким стал cargo у Rust. Сборка, управление пакетами, всякие флаги оптимизации, подпись/публикация пакетов и куча прочего по большому счету не связанного непосредственно с кодом - все из одного места и без большой боли. Опять же я на настолько функциональных языках, как Haskell, длиннее Hello world не писал, поэтому по большей части ссылаюсь на опыт других людей и допускаю, что не так плох чёрт, как его малюют.

Обычно самым большим минусом зовут консерватизм/элитаризм языка ("в 1970е уже все изобрели")

Это точно не про хаскель (да и вообще ни про один из известных мне хардкорных ФП-языков) — туда постоянно что-то допиливают.


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

ХЗ, возможно, тонкости российского хаскелесообщества.


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


опять же я не столько за стандартные библиотеки, сколько за УДОБНЫЕ инструменты каким стал cargo у Rust. Сборка, управление пакетами, всякие флаги оптимизации, подпись/публикация пакетов и куча прочего по большому счету не связанного непосредственно с кодом — все из одного места и без большой боли.

Ну так это, stack же.

Могу предложить вам попробовать Elixir. Сообщество дружелюбное, теоркат знать не требуют, инструментарий удобный: iex, mix, hex.

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

lean'ы и прочие coq'и — прямо программировать на них довольно сложно, и как только вы пытаетесь сделать что-то завтипизированное, они разваливаются и превращаются в боль.


Хотя тактики хороши. Отсутствие адекватного proof automation в агде меня очень печалит.

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

Если вдруг захочется влиться в тему, то можно порекомендовать такие книги:


  1. Type-driven development with Idris — прикладной и практический учебник по идрису, чтобы не выносить себе мозги матаном, а просто более-менее привыкнуть к тому, что термы могут быть в типах.
  2. Verified functional programming in Agda — это всё ещё достаточно прикладной учебник (обсуждения axiom K вы там не найдёте), но уже про то, чтобы доказывать m + n = n + m на агде.
  3. Certified programming with dependent types — это уже книжка по коку, там есть чуть больше обсуждений метатеории языка.
  4. Types and programming languages — такое мягкое введение в теорию типов, с ориентацией на System F, сабтайпинг и формализацию ООПшных языков. Но она очень подробная и дотошная, и её стоит прочитать, чтобы мозги выправить, даже если на сабтайпинг вам плевать. Завтипов вы в ней не найдёте, но если прорешаете все упражнения, то будете крутить деревья вывода утверждений о типах одной левой.
  5. Type theory and formal proof — другое мягкое введение, больше как раз про завтипы, доказательства и всё такое. Но она куда более мягкая и неформальная, чем TAPL.

Я бы рекомендовал последовательность
(1 или 4) ⇒ (4 или 1) ⇒ 5 ⇒ 3 ⇒ 2.

Мне после опыта коммерческого программирования на ширпотребе и хоббийного на Хаскеле первая книга была прям откровением "а чё, так можно было?!"

least butthurt gopher

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

Больше похоже "на вот пять спорных недостатков - давайте к нам в rust". При этом не приводится даже как же эти недостатки в rust решены.

Нет там никакого "давайте к нам в Rust", вы это сами себе придумали. :)

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

Звучит как-то неконкретно. Можно пару примеров?

Мутабельность данных, от которой нельзя избавиться - ключевые слова final, const и иже с ними (val/var) есть давно в примерно каждом языке, на котором я писал и который я помню.

Неумение разделять данные и ошибки, данные и отсутствие данных - всякие там "монады" (на самом деле не во всех языках они реализованы стандартно как честные монады - например, джавовый Optional монадические законы нарушает) Optional/Maybe/Try.

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

В golang есть const. Почему вы и автор статьи вдруг решили что нет? Я встречал исключения java которые отправлялись по почте программисту. Там было 500 Mb текста (просто обернул try все приложение tomcat). Естественно это письмо ему не пришло. Это точно лучший способ обрабатывать ошибки? Был еще такой вариант try, а результат игнорируем. При этом я часто встречал, как на обработку ошибок забивали совсем. К примеру дергаем какую-нибудь функцию из winapi(у которой есть задокументированное возвращаемое сообщение об ошибке) и не обрабатываем ошибку совсем. Потом приходим и делаем мозги сисадмину, как же так - почта не отправляется. Смотрю в логи: а на почтовый сервер никто даже не постучался. По мне так в go не так уж плохо с ошибками, если есть возвращаемая ошибка, то ее нужно явно проигнорировать, либо не обрабатывать никаких возвратов из функции совсем. Есть еще вариант "собирался обработать, но забыл". Но для страховки от таких ошибок сейчас есть инструменты.

Опять же. Сейчас в принципе можно программировать на go в режиме "java-программиста": просто на каждую ошибку вызываем panic и log.Fatal, а в main recover и циклическая перезагрузка приложения, но зачем?

В golang есть const

...который позволяет использовать только compile-time константные литералы:

Constants can be character, string, boolean, or numeric values.

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

Там было 500 Mb текста (просто обернул try все приложение tomcat)

Да, это какой-то абсурдный эдж-кейс, пример чьей-то некомпетентности. Но встречающееся в бОльшей части увиденного мной го-кода data, err := someCall(); if err != nil { return nil, err}лучше только неабсурдностью, практическая полезность всё ещё нулевая.

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

..который позволяет использовать только compile-time константные литералы:

А в c и с++ это по-другому?

Но встречающееся в бОльшей части увиденного мной го-кода data, err := someCall(); if err != nil { return nil, err}лучше только неабсурдностью, практическая полезность всё ещё нулевая.


что вам не нравится в приведенном вам примере? Ошибка не проигнорирована. Она обработана, просто программист решил ничего от себя не добавлять. Может там это и не требуется. Если бы вызвал fmt.Errorf и добавил бы что делал когда ошибка вылезла, было бы лучше? если что там можно указать файл и строку откуда это вызвано, только всегда ли нужно?

Кстати возможно вы имели в виду
if data, err := someCall(); err != nil { return nil, err}

Тогда да. data никогда не будет обработан, err только в случае ошибки, но err = nil никому и не нужен. Но такую ошибку может только новичек совершить, и то, тот что невнимательно вводный курс прошел на сайте.

А в c и с++ это по-другому

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

Go с самого начала наследовал именно синтаксис этих языков с поправкой на упрощение синтаксиса и форматирования.
Динозавры. Так где там rust/python/java/c# для микроконтроллеров? Почему ардуинщики на них не пишут? Почему когда речь заходит про java или micropython, то там речь про ну вот мы написали обвязку вокруг сишного SDK. А где реальные "дрова" оборудования на rust? А чего это никто еще c++ api из windows до сих пор не выкинул и не заменил на .net?
Вот буквально вчера работал в MPLABIDE. И представляешь, там реализация switch на c для микоконтроллеров PIC18 отсутствует.

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


Ваши любимые С/С++ никуда не денутся, не переживайте, поддерживать легаси придётся вечно. Это не отменяет того, что они динозавры (древние и окаменевшие).


Rust для микроконтролеров вполне используется. Не всеми конечно, потому что никто не обязан изучать что-то новое, если не хочет. А теми, кому хочется — прекрасно используется. И «дрова» увидите постепенно. А пока можете полюбоваться на чём пишут новые облачные сервисы в том же Амазоне/Azure — там где идёт передовой край развития производительных и требовательных к надёжности сложных систем.

Rust для микроконтролеров вполне используется.

Не затруднит показать хоть один реальный проект где не мигают светодиодом и не используют spi/i2c. А что нибудь где например используются прерывания и сон устройства. Короче покажите проект на rust(без использования родного SDK) который использует возможности хотя бы одного контроллера на 100 % gpio/i2c/adc/dac/spi/interrupts/sleep/etc. Потому что фреймворков умеющих мигать светодиодом я видел сотни. А вот когда дело касается реальнызх проектов, то увы и ах.

Тут и среди RTOS печальная картина. А когда дело доходит до того чтобы операционка использовала все возможности железа, то не всегда справляется, а если еще учесть что все RTOS на c++ в лучшем случае...

Вот, например, компания накидала целую ОС для микроконтроллеров, используемых как BMC для их компьютеров.

И почему собственно проекты не должны использовать родные SDK, когда язык специально спроектирован, чтобы быть максимально интероперабельным с С (в отличие от того же Go, что, в частности, записывает в недостатки статья, которую мы обсуждаем)?


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

Так SDK написанный, как правило, на c - это все, что способен выполнить ваш код <на современном ЯП>. Т. е. единственная его функция вызвать код на С, а дальше зона ответственности заканчивается, т.е. вы запустили rust, который вроде безопасен, но вся безопасность заканчивается на реализации кода на С за который вы не отвечаете, если там в коде кто-то выудит ваши секреты, вы же не отвечаете? Потому моё отношение такое. Хотите доказать, что ваш язык современен, производителен и безопасен - вперед. Спецификции большинства процессоров со всей периферией давно в широком доступе, но что-то не вижу я распространения таковых в широком доступе, вроде бы есть одна ОС на чистом rust, но и у той распространенность и поддержка железа уступает reaсt os. Равно как и существуют прототипы ОС на go. Черт, я даже драйвер для dht-22 на raspberry pi на go написал. Но утверждать, что за rust или go будущее не берусь. Слишком много "перспективных" языков было похоронено за последние 20 лет.

Назовите «перспективные» языки за последние 20 лет, которые бы претендовали на место С и были бы при этом более безопасными, пользовались поддержкой в индустрии и затем похоронены?

Java. В свое время заявлялась как безопасный кроссплатформенный язык у которого отсутсвуют проблемы с памятью. Существовал вариант и для микроконтроллеров JavaME. Сейчас скорее мертв чем жив. Ну или по крайней мере в микроконтроллерах уступает С. В кровавом энтерпрайзе активно теснится другими языками.

До нее были Delphi/Pascal.

Java или Delphi заявлялись как языки для системной разработки на замену С?

В свое время (середина нулевых) да. Я вот помню, как многие производители писали консоли управления raid-контроллерами на java, телефоны поддерживающие приложения на javame. Чуть позже телефоны отпали. там появились приложения на c/c++ для windows mobile и symbian, но java me долгое время поддерживали устаревшие промышленные GSM модемы и некоторые станки с ЧПУ. Консоли raid-контроллеров кстати тоже очень быстро вернулись на c++.

А, ну и андроид к чему изначально тяготел и был буквально привязан? К java.

Консоль управления, приложения — это не похоже на «языки для системной разработки на замену С»

Android. Я может удивлю, но в свое время некоторые драйверы вполне себе на java писали. Особенно касалось всяких там USB-токенов или весов, или каких-нибудь фискальных регистраторов. Та что там, и прототипы ОС на java были. Так что ничто не ново.

Опять же, что такое консоль управления RAID-контроллером. По большей части это конечно инфа в web-интерфейсе, но есть драйвер и ряд прямых команд этому драйверу вызывающих например создание или перестройку массива, и все это точно работало не через web-api.

Так SDK написанный, как правило, на c — это все, что способен выполнить ваш код <на современном ЯП>. Т. е. единственная его функция вызвать код на С, а дальше зона ответственности заканчивается

Я регулярно пишу код на современном ЯП (правда, ему примерно 30 лет, но неважно), который из C дёргает разве что аллокацию памяти от ОС и файловый I/O.

Т. е. как любой java - код опираясь на java VM?

Да не, я на нативных языках пишу (ну, по крайней мере, тот код, который в итоге хотя бы запускается).

 rust/python/java/c# для микроконтроллеров

за трех последних не скажу, но у раст embed/bare metal есть и цветёт полным цветом. Даже на хабре писали, не говоря уже про официальный шоукейс. Про дрова в статье уже написал - блютус стек андроида, аналогично у Fuchsia, в Линуксе пока на котиках эксперементируют, поэтому в мейнстриме окисленного кода пока нет, хотя Линус уже грозился в следующей версии выпустить немного. На одной из конференций один чувак выступал с рассказом как они ту же ржавчину для спутникового ПО использовали.

> А в c и с++ это по-другому?
да конечно
В с++ можно передать массив как константный указатель на неизменяемые данные и компилятор будет знать что внутри функции его менять нельзя. В го нельзя указать что массив в функцию передается только для чтения.

А в c и с++ это по-другому?

В D определённо по другому.

 там можно указать файл и строку откуда это вызвано, только всегда ли нужно?

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

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

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

Не вижу с этим проблем. Просто ссылка на отладочную информацию будет в нескольких местах.

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

А теперь представьте, что не вы один на проекте, а проект не Hello World, а код писали вообще не вы.

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

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

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

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

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

void begin(const char* hostname, uint16_t port)
...
void begin(IPAddress ip, uint16_t port)

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

Вы предлагаете глазками что ли вычитывать кто кого где и почему вызывает?

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

Если честно, одна из вещей, которые я больше всего не люблю в современном хаскеле — это отсутствие нормального стектрейса в функциях вроде head или tail в стандартной библиотеке. Дёргаешь чей-то код, получаешь *** Exception: Prelude.head: empty list — а где оно? откуда? хрен его знает, где, и надо запускать repl, вспоминать, как там трейсить исключения, писать всякое :set -fbreak-on-exceptions или как его, и так далее.

Так я уже указал. Двух функций с одинаковым именем быть не может. Так что достаточно найти собщение об ошибке, а дальше тех кто его выводит. Если это не твой код, то там как правило сообщения уникальные, тебе остается пронумервать все точки, где это сообщение выводит твой код. Случаи, где return err мне попадаются достаточно редко(как правило это я сам, так я и не делюсь кодом по причине того, что стыдно). Если что-то выкладываю, то стараюсь чтобы там было все обработано.

Что, и имена методов тоже все уникальные для всех объектов?

В го же нет методов и объектов в привычном понимании адептов ООП. Но да, для одного "объекта" не может быть двух методов с одним именем. Т. е. будет:

OpenbyHostname(hn string)
....
OpenbyIP(ip net.IP)

вместо

Open(hn string)
....
Open(ip net.IP)

Речь про разные объекты. Не делайте вид будто не понимаете.

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

Всё нормально. Не вижу с этим проблем.

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

А в c и с++ модификатор const можно добавить к любому типу данных, и инициализировать любым значением.

Constants can be character, string, boolean, or numeric values.

Какого стандартного типа go тут не хватает. struct? А точно в c struct может быть константой? И часто встречаются константы такого типа?

Ну так ключевое слово const к константам никакого отношения не имеет, так уж сложилось исторически.

А в c и с++ это по-другому?

Да, конечно.


int main(int argc, char**)
{
  const int dunno = argc;
}

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

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

В с ваш пример не компилируется. в С++ работает, но есть ньюанс "не константа, а read-only variable" , тогда так https://stackoverflow.com/questions/47632706/immutable-struct-in-golang

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

Таким образом. Все чего яко-бы нет в <язык программирования> на самом деле есть, но как правило делается способом отличным от того, что имел в виду <автор утверждения>, и от того ему лично неудобным. Это кстати относится к любому срачу: Windows vs Linux, MSO vs LO etc.

В с ваш пример не компилируется.

Всё там компилируется.

GCC 12, да но 10 уже нет.

<source>: In function 'main':
<source>:1:20: error: parameter name omitted
    1 | int main(int argc, char**)
      |                    ^~~~~~
Compiler returned: 1

Могли бы догадаться написать там argv. К константности это не имеет никакого отношения.

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

Развести горы копипасты вместо написания одного слова immutable - очень по гошному, да.

Вы же понимаете, что с подобным подходом вы создаете богатую почву для критики любого ЯП?
Развести горы копипасты для <действия>, которое я у себя делаю в два клика.
Например в go мне не нужно начинать Hello World с создания класса или объявления namespaces как в C#. Я же не заявляю что шарперы развели копипасту.

Есть не большая разница между "плюс 2 строки" и "в 2 раза больше строк".

откуда взялось "в два раза больше строк"? У вас есть проект целиком состоящий из констант, которые непременно нужно изменять при старте?

Вроде обязательное форматирование табами столько места не занимает.

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

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

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

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

Вам язык явно мешает это делать. Костылями мы все умеем пользоваться. Самыми разными.

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

Ага 100500 одинаковых методов для чтения значений, прекрасно.

В Джаву завезли специальную языковую конструкцию чтобы дать возможность делать сложные иммутабельные объекты. Которые естественно можно инициализировать чем угодно.

Ну да, кому они нужны?

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

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

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


Как мне в go писать монадические парсеры?

А нужно? Для этого есть более подходящий язык? Т. е. на go или любом другом языке можно решить конкретную задачу в особо упоротых случаях можно вызвать c или asm, но язык все так же остается инструментом для решения конкретной задачи.

Я отвечу почему я им занимаюсь.
1. Простой. Я не профессиональный программист пришел к go когда для моих задач(системное администрирование/DBA, windows + linux стало не хватать обычных скриптовых языков вроде cmd/sh/bash).
2.Когда понадобилось копировать по сети за пределами os.copy
3. Когда понадобилось копирование множества файлов одновременно.
4. Когда понадобилось запускать свой код на компах где нет нужного рантайма(Можно на .net,вот только тогда еще .net не работал под linux. И у меня был зоопарк систем от windows NT 4.0 до windows 2012, и от centos5 до debian8)
5. когда потребовалось за короткий срок написать tcp-сервер принимающий множество сообщений одновременно.

6. Когда понадобилось сделать парсер для KX-TDA200 c составлением отчета о звонках.

7. А дальше пошло-поехало Modbus-устройства, расширение modbus-tcp, реверсивный modbus и мосты к сторонним системам, modbus over GSM....

8. А теперь все то же но на Arm....

А нужно? Для этого есть более подходящий язык?

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

А в чём проблема? Пишете return, join, и вперёд. Руководств по монадам на Go предостаточно.

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

что вам не нравится в приведенном вам примере? Ошибка не проигнорирована. Она обработана

Ну тогда джавист из вашей истории, обернувший всё тело программы в трай-кэтч, тоже обработал ошибку, чо. Тоже сделал rethrow.

  • Проверяемые компилятором исключения в Java.

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

checked exceptions в джаве кстати как раз одно из спорных и непопулярных решений. Другой вопрос, что даже оно лучше, чем то, что наворотили в го.

А что там спорного? Хомячкам говнокодить не даёт?

try-catch-based логика отвратительно читается, хуже тестируется и вообще более error-prone в моих глазах, чем более "чистые" в смысле флоу кода механизмы возврата и обработки ошибок. А ещё есть очень много откровенно unrecoverable ситуаций.

Чуть более развёрнуто есть тут, например.

try-catch-based логика отвратительно читается

Не хуже чем if, switch, for и тд. Претензию тут можно предъявить разве что к тому, что во многих языках зачем-то сделали, что переменные, объявленные в try не доступны в catch.

хуже тестируется

Это о чём вообще?

 более "чистые" в смысле флоу кода механизмы возврата и обработки ошибок.

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

 А ещё есть очень много откровенно unrecoverable ситуаций.

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

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

Что там плохо читается? do, и все.


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

Раст не является образцово дружественным к монадам языком.

Дружелюбные к монадам языки не дружелюбны к массовому программисту.

К массовому программисту вообще много что недружелюбно. Тяжело быть массовым программистом.

У ифа, свитча, фора, и прочих куда более очевидная логика перехода/ветвления, чем у try-catch. У них это "проверь условие на входе - неверно - иди в другой блок [и проверь условие там, если надо]". У try-catch это "видишь вот этот блок и абсолютно всё, что в нём вызывается? В любой момент абсолютно что угодно из блока этого может сделать плохую магию, и ты перескочишь вот в один из тех других блоков" (кэтчей-то бывает несколько).

А ещё если у трая большое тело, контекст теряется только в путь.

Захотели потестировать? Убедитесь, что интересующий вас эксепшн в трай-блоке тестируемого кода кидает та самая строка, о которой вы думаете, а не другая на 10 выше/ниже/глубже, потому что иначе вы не знаете, что вы протестировали на самом деле.

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

Вы высасываете из пальца. В try передаётся блок кода (размер которого вы контролируете сами), который либо исполнится как ожидается, либо будет ожидаемый переход в catch с пояснениями. Монады работают точно так же.

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

либо будет ожидаемый переход в catch

В какой момент кода и в каком состоянии системы он произойдёт? :)

Тестировать надо поведение, а не строки.

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

Не поверите, в случае любой исключительной ситуации.

Зачем вы проверяете поведение зависимостей? Что вы будете делать, когда зависимость поменяется? Будете переписывать все тесты?

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

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

НЛО прилетело и опубликовало эту надпись здесь

Такая себе аргументация. Вместо улучшения механизмов обобщённого программирования, вкрутили новую фичу без поддержки старой.

НЛО прилетело и опубликовало эту надпись здесь

С демагогией - это не ко мне.

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

Давайте посмотрим на классику — интерфейс Readable из JDK

int read(CharBuffer cb)
throws IOException

Throws:
IOException — if an I/O error occurs
NullPointerException — if cb is null
ReadOnlyBufferException — if cb is a read only buffer


Заставляет ли джава обработать все ошибочные сценарии? Нет, ей наплевать на ReadOnlyBufferException. Заставит ли меня каждый вызов оборачивать в try/catch? Да, хотя вызывающему коду в 90% случаев вообще наплевать, что там сломалось — не смог прочитать и не смог, залоггировал и дальше поехали (при этом язык никак не мешает хомячкам бахать пустой catch и съедать исключения).

Если вам мало — можете заглянуть в класс Cipher. Там вообще красота

public final int doFinal(byte[] output,
int outputOffset)
throws IllegalBlockSizeException,
ShortBufferException,
BadPaddingException


Note: if any exception is thrown, this cipher object may need to be reset before it can be used again.

Throws:
IllegalStateException — if this cipher is in a wrong state (e.g., has not been initialized)
IllegalBlockSizeException — if this cipher is a block cipher, no padding has been requested (only in encryption mode), and the total input length of the data processed by this cipher is not a multiple of block size; or if this encryption algorithm is unable to process the input data provided.
ShortBufferException — if the given output buffer is too small to hold the result
BadPaddingException — if this cipher is in decryption mode, and (un)padding has been requested, but the decrypted data is not bounded by the appropriate padding bytes
AEADBadTagException — if this cipher is decrypting in an AEAD mode (such as GCM/CCM), and the received authentication tag does not match the calculated value


Три проверяемых исключения, которые в 90% случаев бесполезны, и еще 2, которые всё равно уронят весь код, если вы их не обработаете руками, и которые так же бесполезны.

Заставляет ли джава обработать все ошибочные сценарии? Нет

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

Заставит ли меня каждый вызов оборачивать в try/catch? Да

Не врите.

не смог прочитать и не смог, залоггировал и дальше поехали

if в данном случае ничем принципиально от try-catch не отличается.

Три проверяемых исключения, которые в 90% случаев бесполезны,

Их хорошо бы объединить в CipherExceotion.

и еще 2, которые всё равно уронят весь код

Ну так, лучше бы выпилили UnchekedException вообще.

Заставлять она и не должна.

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

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

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

Не врите.

NO U.

Ну так, лучше бы выпилили UnchekedException вообще.

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

Не пытается и не заставляет. Чего вы фантазируете?

Джавадоки не проверяются компилятором.

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

Не пытается и не заставляет. Чего вы фантазируете?

class Scratch {
    public static void main(String[] args) {
        ByteArrayInputStream stream = new ByteArrayInputStream(new byte[] { 1 });
        stream.close(); // unreported exception java.io.IOException; must be caught or declared to be thrown
    }
}

Или вы мне сейчас попытаетесь сказать, что ошибка компиляции — это «уведомить»?

Джавадоки не проверяются компилятором.

Вы начинаете понимать суть проблемы.

Ага, круглые колёса — это плохо

В том и дело, что они не круглые, а полукруглые.

Ну и где тут обёртка?


class Scratch {
    public static void main(String[] args) throws IOException {
        ByteArrayInputStream stream = new ByteArrayInputStream(new byte[] { 1 });
        stream.close();
    }
}
Плохих языков не бывает

Бывает, ещё как бывает. C — плохой язык. C++ — плохой язык. Go — плохой язык. 1С, прости г-пди — плохой язык.

А хорошие бывают?

Нет. Бывают чуть менее плохие.

Странно…

Солидарен с Дедфудом: хороших нету, есть лишь менее плохие.

Для контроллеров на чём писать? Если не С то что?

BetterC

На Rust

Я имею ввиду 8 битники. AVR, PIC и прочее. Там чуть ли не на асме надо писать

https://github.com/avr-rust — есть же что-то и под эту мелочь для Rust’a. C принципиальной точки зрения ничто не мешает писать на Расте и получать тот же код в бинаре, что и при написании на асме.


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

А для микроконтроллера какие требования? Он же не будет торчать в открытый интернет

О, держите оптимиста.

Что и 8-битники в интернет пускают? А там поместится TCP-стек-то хотя бы? (расписался в собственном полном незнании темы, лол)

Есть uTCP. Туда вообще умельцы много чего помещают. Например, виртуальную lisp-машину с реализацией http-сервера поверх неё.

Я когда то писал TCP/IP стек на С. Даже писал ещё ниже уровень PPP А только поверх него уже IP и далее TCP. Было очень давно, подробностей не помню, но кода получилось довольно мало. Это было во времена первых GPRS модемов. В жирный AVR уместилось бы. Конечно вероятно не все ситуации код мог переварить, но минимальный рабочий вариант получился. Там не так страшно — пришедший пакет упакован как матрёшка из данных. Просто надо найти адреса последовательно где какой протокол начинается и добраться до данных. Сами пакеты TCP/IP примитивны и просты.

Круто!


Я ж не говорю, что это невозможно, мне просто интересно — делают ли так на самом деле :) Ну и что там на таком МК ещё поместится, если он будет в сеть торчать и что-то обрабатывать? То есть одно дело, когда у тебя сложная система, что-то там из сети прилетает, что может поломать и использовать во зло и таких примеров просто бесконечность. А другое дело, самый мелкий МК с прошивкой в пять тысяч строк и даже торчащий в интернет — ну даже сломали его, и? Лампочкой помигают за автора? :)


А уже на какой-нибудь ESP32 Rust вполне как дома.

Ну контроллеры ставят во всякие железячные устройства. Лампочкой поморгать или ещё чего. Или наоборот — отослать сигнал. Там и требуется только например по GPRS отослать на какой нибудь IP булеву переменную, что у тебя например огурцы на даче поливать надо ))) Хотя сейчас например даже в самом захудалом GSM модеме свой TCP стек.

Если контроллер только для передачи сигнала через net — то там нечего ломать. А если на приём. То я не сильно представляю где голый контроллер может в сеть смотреть. Это либо всякие свичи и модемы для оптики или ещё что. У всего этого как правило есть свои настраиваемые фаерволы или криптотунели.

P.S. Сначала написал комент про лампочку, а потом прочитал ваш ))) Короче все думают про лампочку ))) А Выбор AVR или там STM32 — только в цене по мойму. Берут что подешевле. Хотя STM раньше стоил дешевле AVR — что меня очень удивляло. Сейчас не знаю как.
Он же не будет торчать в открытый интернет

зато всякие неожиданные значения на входах — обычное дело

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

Материал о психотерапии, достаточно ознакомиться с группой сочувствующих и пострадавших в комментариях к оригиналу. Остается догадываться вызывают ли у них эти муки наслаждение или все же присутствует некий элемент понуждения. Трудно представить домохозяйку, бывшую владелицу пылесоса Самсунг, ныне гордую обладательницу Philips, которая годами пишет эссе о том, как плох Samsung, как люди себя обманывают продолжая использовать корейскую технику. Это патология или травма.

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

кто всем сердцем соглашаются с моей филиппикой

Здесь подразумевается монета? Или опечатка?

В Go нет тип-сумм — и поэтому там реально неудобно написать тип, который представлял бы «либо адрес IPv4, либо адрес IPv6

В большинстве случаев такое спокойно решается через type switch. Но тут, думаю, нужно изначально проектировать по-иному — напр. работать не с конкретными типами, а с интерфейсами, и тогда без разницы что под капотом — ipv4, ipv6 или номер квартиры.


нo не допускает перегрузки операторов, напоминая о той давней истории Java, когда a == b не было идентично a.equals(b)

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


В Go отсутствует поддержка неизменяемых данных. 

По старинке — обернуть в read-only интерфейсы. Вообще мне больше импонирует идея view-интерфейсов, т.к. они более гибкие, чем какой-то один прибитый гвоздями механизм на уровне языка.


В целом я бы сказал, что для 90% случаев существующие примитивы — good enough. В 10% случаев нужны танцы с бубном. В каких-то языках из-за перфекционизма танцы с бубном будут больше 10%, зато всё теоретически красиво. Всё-таки, Go замышлялся как простой язык.

Всё-таки, Go замышлялся как простой язык.

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

Первый же пример абсолютно нелеп и притянут за уши -- Го серверный язык и работа с файловыми атрибутами на винде ему нужна как собаке пятая нога. И что мы видим как контрпример? Естественно, раст! Опять же, без комментариев. Само собой, это просто объективная критика Го, а никакая не попытка пропихнуть Раст. Да, всё так.

Го серверный язык и работа с файловыми атрибутами на винде ему нужна как собаке пятая нога.

  • Тогда можно а) не поддерживать Windows б) не поддерживать соответствующие файловые системы

  • "Го серверный язык" где написано? На оф.сайте го не вижу ни слова о том, что на винде его запускать нельзя или не нужно (более того, я сам в продакшн код на го из-под винды писал).

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

Я прочитал первую статью ещё когда она вышла, а вторая только укрепила моё мнение относительно автора и его мотиваций. Заглянул тут ещё ради смеха на ютуб канал автора -- раст, раст, раст. Один сплошной раст. Для меня всё очевидно как божий день. Но если кто-то верит в то, что автор просто вдруг на ровном месте решил pro bono покритиковать Го, это его дело.

А почему в русскоязычном комьюнити так оголотело не любят раст? Есть какая-то фундаментальная причина или это просто технологическое отставание от запада?

Это вопрос к сообществу, а я обсуждаю конкретную статью, где под видом критики Го я вижу пропихивание Раста. Вот за такое, очевидно, и не любят.

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

Специфическое отношение у кого? У вас лично?

Нигде никакого массового "специфического отношения" к расту я не видел.

А я вижу везде -- на хабре, опеннете, ЛОРе, твиттере, реддите, хакерньюс, you name it. Далеко ходить не надо: https://www.google.com/search?q=why+people+hate+rust

Далеко ходить не надо: https://www.google.com/search?q=why+people+hate+rust

А теперь подставьте туда java, javascript, php, python и go (golang к сожалению недостаточно распространённое название - а зря).

Вы-таки не поверите, но побеждают JS и PHP - а технически как раз go, но там слово очень уж неудачное - а у Раста выдача, подозреваю, чуть больше, чем у той же джавы, просто потому, что Rust - это ещё и название сравнительно популярной компьютерной игры.

При чём тут гугл? Эта ссылка была просто для примера, а основанием для комментария является мой личный опыт, потому что я сижу на всех перечисленных ресурсах. Везде к расту одинаково полярное отношение -- или фанатичная любовь или ненависть. И даже эта статья, которая вроде бы должна была быть о Го, превратилась в срач о расте. В общем-то, как я уже написал, я сказал по этой теме всё, что хотел, ещё в самом первом своём комментарии, добавить нечего, и переливать из пустого в порожнее я не любитель, тем более, что есть ограничение по карме. Рынок всё порешает.

При чём тут гугл? 

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

Эта ссылка была просто для примера

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

И даже эта статья, которая вроде бы должна была быть о Го, превратилась в срач о расте.

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

Конкретно на хабре есть несколько шумных нелюбителей, для которых любое сравнение это «пытаются впарить свой сраный %подставитьязык%». А так нормально всё.

если кто-то верит в то, что автор просто вдруг на ровном месте решил pro bono покритиковать Го

Не "на ровном месте", а (как написано в статьях) потому что он активно на нём писал - в том числе на работе - и пытался с ним "подружиться".

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

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

Я вот тоже сижу и критикую Го в комментариях на хабре, и вы не поверите, никакая масонская ложа мне ничего за это не платит.

Ну вот про файлы, chmod и Windows это прям... не уникальная проблема.

Вот к примеру боль автора питоновского модуля oschmod:

Even though Python's os.chmod() sets read, write, and execute file permissions, on Windows, os.chmod() basically has no effect. Even worse, Python on Windows gives no warnings or errors. If you think you set file permissions on Windows with os.chmod(), you're wrong!

Ну то есть буквально в Go скопировали поведение Python.

Хорошо ли это? Нет, могли бы и нормально сделать.

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

Стоило ли из-за этого "не поддерживать Windows"? Нет. По крайней мере ровно в той степени, в которой тогда надо "не поддерживать Windows" в Python. Остальное то (наверное) работает. Nice небось тоже на Windows кривой почти везде и молча делает ничего, но ничего, пока никто не расстроился.

Ну да, я не питонист.

Хорошо ли это? Нет, могли бы и нормально сделать.

Ага, всё так.

Можно ли это понять? Да, можно

Нет, не можно, когда вы делаете строго типизированный язык (я тут могу много материться по поводу interface {}, но язык всё-таки строго типизированный) в конце нулевых, когда, помимо другого подхода к типизации, мир и концепции дизайна ЯП несколько отличаются от тех, в реалиях которых Гвидо дизайнил Пайтон или даже тех, в которых он довыпускал Python 2.2, в котором появился chmod.

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

Так а причем тут строго типизированный язык, если я говорю об одном конкретном наезде про chmod?

Наговнокодить в кросс-платформенной реализации чего либо достаточно просто. Особенно если эта платформа не является целевой. В целом странно ожидать, что chmod будет работать в windows, где в принципе нет битовой маски свойств, правда? Но наверно ошибочку таки стоило бы писать.

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


Если бы пакет, предоставляющий Chmod, назывался бы не "os", а "os.unix" или "os.linux" — претензий бы к нему было меньше.

Если не лениться читать, то в описании пакета os в Go написано следующее:

Package os provides a platform-independent interface to operating system functionality. The design is Unix-like, although the error handling is Go-like; failing calls return values of type error rather than error numbers. Often, more information is available within the error. For example, if a call that takes a file name fails, such as Open or Stat, the error will include the failing file name when printed and will be of type *PathError, which may be unpacked for more information.

The os interface is intended to be uniform across all operating systems. Features not generally available appear in the system-specific package syscall.

The os interface is intended to be uniform across all operating systems. Features not generally available appear in the system-specific package syscall.

Установка прав доступа через битовую маску — это и есть та самая "feature not generally available", но в os она таки почему-то есть.

Тут дилемма — если наваять most common denominator, т.е. только те фичи, которые есть во всех OS, то API работы с окружением будет ограничено в фичах, от этого страдала Java, например. Другой вариант вообще не делать общего API, но тогда из коробки вообще ничего нет. Выбор Unix-поведения как дефолтного считаю на практике обоснованным, т.к. целевая аудитория Go это всё-таки Unix-подобные системы, которых большинство, и Windows тут скорее исключение, чем правило. Такова реальность — в итоге в самом Windows добавили WSL, posix compatibility layer; инструменты разработчика портируются с Linux на Windows, и не наоборот.

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

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


Тут, думаю, нужно впервую очередь смотреть на практику. В целом, на Go довольно просто писать код. Но доказать, что код корректный, сложнее, чем, например, в Rust. И тут мы упираемся в то, что по большому счёту все холивары основываются на субъективных ощущениях, интуиции. Очень мало у нас опираются на исследования и метрики по поводу того, как та или иная фича влияет на разработку. А иначе мы просто топчемся на месте. Недавно открыл для себя термин evidence-based software engineering, по нему есть литература, напр. одноимённая книга Дерека Джонса.

В целом, на Go довольно просто писать код.

Ну вот в том и дело, что автор статьи (как и я) писал на Го код профессионально довольно продолжительное время, и пришёл к совсем другим выводам. Я с автором в этом согласен.

Погуглите ещё формальные методы и automated reasoning tools. И вот семантика раста и сложившееся вокруг него коммьюнити оказались весьма подходящими для того, чтобы такие тулзы делать. Убойный набор — язык, который проверяет множество инвариантов + мощное тестирование (property based testing и прочий fuzzing) + формальная верификация = безбажный софт.

Только в Марте этого года (а ему сколько уже, 12 лет?) в Go добавили generics и этим все сказано.

Для тех кто не работал с Go, а что все сказано? Прошу кэпа на помощь. То что изначально авторы языка говорили дженериков и шаблонов не будет, а теперь сдались? Типа он уже никакой не минималистичный и простой? Или типа вот только в марте добавили, поэтому все посты про то что в Go нет нормальных возможностей уже надо закапывать? Я не особо понимаю как это конфликтует с оригинальной статьёй.
Эм? я знаю что такое generics, вопрос не в этом. что «этим всё сказано».

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

Нет, не знаю. Просвятите? Вы и правда считаете, что я от нечего делать спрашиваю о том, что знаю сам? Смысл какой?

То есть вы не знаете, что такое обобщённое программирование. Узнать о нём вы можете по ссылке выше.

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

Давайте сначала. Я C++ разработчик. GP я вполне себе на каком-то уровне уменю в этом языке.
Я читал новость о том что в Go добавили generic-и, выглядит как что-то (на совсем поверхностный взгляд незнакомого с Go) позволяющее работать в GP парадигме. Насколько это соответствует мощи шаблонов С++ — не берусь сказать. Возможно, не соответствует. В любом случае что
а) в Go все нормально сейчас с GP
б) или в Go добавили generics но их сильно мало
я все еще не понимаю, как эта информация что их уже добавили в этом году соотносится с «этим все сказано», с какой частью статьи это коррелирует.

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

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

Это миф про то, что "принципиально" не хотели. Если поискать через wayback machine, то официальный faq уже в 2013 году (раньше не нашёл) писал о том, что они открыты к добавлению дженериков, но пока нет понимания, как лучше реализовать.

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

Авторы создали Go, потому что устали ждать долгой компиляции проектов на C++ в проектах Google. Изначальная идея Go была в том, чтобы это был простой good enough язык, который легко масштабируется на большие кодовые базы at scale и при этом быстро компилируется. По сути основные первоначальные решения отталкивались именно от этого — быстрая компиляция больших проектов (что иронично, когда сейчас один из основных use case'ов Go это микросервисы). Отсюда запрещены циклические зависимости, поэтому в язык добавлен structural typing, поэтому нет наследования, поэтому местами синтаксис немного странный — для быстрого парсинга, и т.д. Дженерики, шаблоны — они значительно могут замедлить время компиляции и сильно раздуть код (code bloat), если их неправильно приготовить. Поэтому много времени ушло на то, чтобы найти правильный баланс, т.к. авторы, видимо, были травмированы опытом в C++. Go часто по фичам сравнивают с Rust, но там вроде время компиляции не возведено в абсолют, и я слышал истории, что в Rust со скоростью работы компилятора не так всё гладко. То, что авторы Go в рантайме в итоге реализовали для оптимизации компилятора, похоже на generic code sharing при реификации дженериков в C#, которые там давно есть. Странно, что так долго к этому приходили, возможно были трудно решимые проблемы с семантикой/синтаксисом, чтобы было backward compatible. Т.е. тут основной косяк авторов в том, что первоначальный драфт языка не проектировался с учётом расширения его под дженерики, напр. теперь map[int]int смотрится странно.

Мне, конечно, до авторов Go далеко, но, вот, всё же, зря они пошли на поводу у сообщества. Добавили бы лучше generic functions. И синтаксис такой бы вписался бы лучше в язык, и по выразительности они достаточны, и по производительности хуже не были бы, учитывая текущую реализацию generic-ов.

Всё-таки основной use case дженериков это обобщённые контейнеры. Из-за этого по факту в Go всего два типа контейнеров — слайсы и мапы, что далеко от идеала. Как наличие только generic-функций решило бы этот вопрос?

С++ компилируется долго из-за кривого дизайна, унаследованного от C. Go корректней сравнивать с D, который и компилируется быстро, и дженерики были сразу, а не через 12 лет. И много чего ещё умеет, чего не умеет Go.

Многие хвалят D, но остаётся подвисшим вопрос — почему он не взлетел, если он настолько хорош? Был аргумент, что за ним не стояла большая корпорация, но за последнее время появилось несколько языков вроде Zig, к которым сейчас, субъективно, больше интереса, чем к D. Дело ещё не в языке, а в тулинге например. Может быть, с тулингом что-то не так? В Go много хорошего, что есть из коробки, не касается языка как такового.

почему он не взлетел, если он настолько хорош?

  • Если за холмом трава зеленее, то почему все прыгают с обрыва?

  • Ну все туда бегут и я побежал.

Может быть, с тулингом что-то не так? В Go много хорошого, что есть из коробки, не касается языка как такового.

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

Я бы не сказал, что чистый Си компилируется медленно. Полчаса для проекта размера ядра Линукс это немного. Плюсы это другой разговор. Так что аргумент про кривой дизайн требует как минимум раскрытия.

Это не кривой. Просто в D лучше, спору нет.

Заново парсить файл в каждом месте его включения - не криво? А что же тогда криво?

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

Это неправда.

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

Дженерики как в джаве сильно нагружают гц и лишают вас возможности нормального дебага в рантайме.

Дженерики как в плюсах замедляют время компиляции, что для больших проектов критично.

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

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

Дженерики как в джаве сильно нагружают гц

Чем, пардон, джавовые дженерики нагружают ГЦ больше, чем любые другие?

лишают вас возможности нормального дебага в рантайме

Во-первых можно делать их reified (хотя кмк это поощряет некоторые сомнительные практики в разработке, но не суть), во-вторых, и с type erasure всё в рантайме прекрасно дебажится.

Дженерики как в плюсах замедляют время компиляции

Ну вот только в плюсах не дженерики, угу. Шаблоны - несколько не то же самое.

Чем, пардон, джавовые дженерики нагружают ГЦ больше, чем любые другие?

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

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

Не факт. Вы не учитываете усложнение алгоритма вывода типов. БОльшая часть правил вывода в том же Haskell, процентов 90, - она про ограничения и generic-и (конструкторы типов) как раз. А работает проверка и вывод типов всегда. И там сложность, минимум, квадратичная.

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

Так необходимо вывести instance.

Или указать явно. Это ортогональные вещи.

Так явное указание требует вывода и проверки ограничений, всё равно.

Сверка типов - тривиальная операция. Вывод тут никакой не нужен.

Вывод типов в haskell98 очень простой и тупой. Сложно (неразрешимо сложно) выводить типы, когда у вас есть всякие type families, GADT'ы и прочее.

Обобщение в широком смысле слова — это ключевая аналитическая задача программиста в процессе разработки решения и моделирования предметной области.   

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

Кроме технической составляющей, я бы еще отметил, что адепты Go все эти 12 лет себя и окружающих фанатично убеждали что дженерики не нужны "by design". Однако, реальная практика всё же вынудила признать некомпетентность изначальных идей.    

Этот пример с дженериками достаточно очевидный и показательный чтобы отметить в целом всю несостоятельность и глупость дизайна Go. Поэтому используется такая фраза как "этим всё сказано".  

Ваш капитан очевидность. 

Этот пример с дженериками достаточно очевидный и показательный чтобы отметить в целом всю несостоятельность и глупость дизайна Go. Поэтому используется такая фраза как «этим всё сказано».

Ваш капитан очевидность.

Ну спасибо что хоть кто-то уделил минутку внимания чтобы объяснить что там сказано)

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

А, еще видимо я слово «только» не в том значении прочитал.

Я прочитал предложение как "… статья конечно хороша, но вот только в Go уже есть дженерики с марта"
а по факту коммент был похоже
«плюсую статье, все по делу. одно лишь факт (»только") что в марте ЭТОГО года добавили..." далее по тексту.
Возможно из-за этого и был тупняк.

Всем извините, можете минусить мой тупизм дальше)

Дело не просто в том, что не было дженериков, а в том, что было: статическая строгая типизация. Сразу понятно к чему это приводило - когда реализации одного и того же метода интерфейса для разных типов приходилось писать вручную, есть целые библиотеки с кучей функций типа AddIntInt, AddInt32Int32, AddUint32Uint32 и т.д. И кодогенерация, как в "старых добрых" 60-х. Хотя уже тогда были языки с динамической типизацией вроде разных LISP, а Smalltalk появился в 1972. А Go притворяется, что сейчас 70-е. Это такой Паскаль с CSP. Со всеми вытекающими.

Как программисту на С++ мне это слушать как страшную сказку приходится) Сочувствую Go-разработчикам. Не, строгость это хорошо. Это одобряю. Но вот этот вот AddUint32Uint32 ужасно же.

Можно ещё вспомнить классику.

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

* За тупизм извиняем.

Вот Вы говорите "наиболее популярный и эффективный". А на какой статистике Вы основываете это утверждение?

Надеюсь, вы этот вопрос написали истины для, а не потроллить. 

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

Возьмем индекс TIOBE и посчитаем популярность языков: 

Python + Java + C# + Visual Basic + Swift + PHP = 38%
C + JavaScript + ASM + SQL + Go + C++ = 29%

Чисто формально по этим цифрам дженирики наиболее популярный способ даже против суммы всех "не дженериков", а если сравнивать по отдельности, вывод будет еще основательнее.

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

Изначально в go были ограниченные дженерики -- слайсы, мапы и каналы. Но свои собственные обобщённые типы программисты создавать не могли.

Ну как добавили... как и почти все остальное в го они half-baked:

type Integer interface {

~int | ~int8 | ~int16 | ~int32 | ~int64

}

до функционала дженериков в TS, расте или других подобных языках им еще 12 лет...

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

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

Я давно слежу за разработкой go и это совсем не то, впечатление, которое я получил. Как минимум, каждый год проводится survey для понимания приоритетов сообщества, я участвую в каждом. Отсутствие дженериков было обозначено как проблема номер один. Любой желающий может предложить proposal. Проблема в том, что большинство proposals ещё более half-baked, чем то, что предлагает ядро разработчиков из Гугла. В стиле "я видел фичу Х в другом языке, хочу так же". Детали не проработаны, backward compatibility не проработано, и т.д.

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

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

А зачем писать комментарии, если получается написать только "Не нравится - не ешь"? :)

Критику можно обсуждать, опровергать, соглашаться, вот это вот всё. Это в конце концов позволяет сделать язык лучше. Но нет же. :)

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

(с) Ф. Кривин

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