Что: 2e27d5c264f5b59374b193e972190ff3af53bbc6
Когда: 2022-11-14 09:49:50+03:00
Темы: go
Роб Пайк об истории появления Go: less is more https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html Какие упрощения были сделаны относительно C/C++ того времени? * regular syntax (don't need a symbol table to parse) * garbage collection (only) * no header files * explicit dependencies * no circular dependencies * constants are just numbers * int and int32 are distinct types * letter case sets visibility * methods for any type (no classes) * no subtype inheritance (no subclasses) * package-level initialization and well-defined order of initialization * files compiled together in a package * package-level globals presented in any order * no arithmetic conversions (constants help) * interfaces are implicit (no "implements" declaration) * embedding (no promotion to superclass) * methods are declared as functions (no special location) * methods are just functions * interfaces are just methods (no data) * methods match by name only (not by type) * no constructors or destructors * postincrement and postdecrement are statements, not expressions * no preincrement or predecrement * assignment is not an expression * evaluation order defined in assignment, function call (no "sequence point") * no pointer arithmetic * memory is always zeroed * legal to take address of local variable * no "this" in methods * segmented stacks * no const or other type annotations * no templates * no exceptions * builtin string, slice, map * array bounds checking We also added some things that were not in C or C++, like slices and maps, composite literals, expressions at the top level of the file (which is a huge thing that mostly goes unremarked), reflection, garbage collection, and so on. Concurrency, too, naturally. Ну и заканчивает это всё очень правильными высказываниям Python and Ruby programmers come to Go because they don't have to surrender much expressiveness, but gain performance and get to play with concurrency. C++ programmers don't come to Go because they have fought hard to gain exquisite control of their programming domain, and don't want to surrender any of it. To them, software isn't just about getting the job done, it's about doing it a certain way. The issue, then, is that Go's success would contradict their world view. And we should have realized that from the beginning. People who are excited about C++11's new features are not going to care about a language that has so much less. Even if, in the end, it offers so much more.
From: kmeaw Date: 2022-11-14 09:42:15Z * no "this" in methods Кажется, не получилось. Или я не понимаю, в чём принципиальное отличие receiver от this. * no const or other type annotations * no exceptions Очень спорные решения, не уверен, что от этого становится проще и лучше. А вот всё остальное - очень правильные решения, которые резко снижают когнитивную нагрузку при написании (и чтении!) кода без ущерба для производительности и выразительности. Есть ещё одна вещь, которой не хватает - context уровня языка, а не стандартной библиотеки.
From: Sergey Matveev Date: 2022-11-16 09:13:55Z
From: kmeaw Date: 2022-11-16 12:39:00Z > >* no const or other type annotations > но в голове думая про то, что это поможет компилятору Это ещё и защищает от ошибок программиста. memcpy(dst, src, n) не даст перепутать src и dst в большинстве случаев, потому что src - указатель на константу. А в C++ ещё и const-методы бывают. По сигнатуре сразу видно, изменяет вызов данные или нет. > я всецело за if err != nil код Я вижу три проблемы. Первая - замусоривание кода одинаковыми строчками вида if err != nil { return nil, err }. От этого при чтении замыливается глаз, и легко пропустить if err == nil в этом потоке. Вторая - язык не делает ничего, чтобы вместо if err != nil { return nil, err } заставить меня написать if err != nil { return nil, fmt.Errorf("cannot save config: %w", err) } Поэтому почти все используют первый вариант. Ведь в большинстве случаев нужно просто выполнить какую-то последовательность действий, а если любое из них не удалось, то просто сломаться. И если такой код становится библиотечным, то ко мне прилетает необёрнутая ошибка типа io.ErrUnexpectedEOF, которая никак не подскажет пользователю, что пошло не так. С исключениями такого не будет - если вызываемый код не умеет обрабатывать какую-то исключительную ситуацию, то исключение рано или поздно долетит до catch-all обработчика, у которого будет вся контекстуальная информация, включая стек вызовов - даже если просто его распечатать, это очень поможет пользователю понять, что пошло не так. Третья - механизмов работы с ошибками два, есть ещё panic. Когда первый раз читаешь документацию, то кажется, что всё просто и очевидно. Но когда программа развивается, становится всё сложнее, и, наконец, превращается в библиотеку, которая используется другой программой, а прежний main - в метод Init(), то приходится рефакторить: все log.Fatalf заменять на return fmt.Errorf, добавлять второе return value для ошибок и протаскивать их наверх по стеку. Исключения также могут оказаться более эффективными по производительности, чем десятки проверок на err != nil. Хотя и не любят их по той же причине - они могут коварно скрыть увеличение времени исполнения в плохом сценарии, из-за чего тяжело давать гарантии в системах реального времени.
From: Sergey Matveev Date: 2022-11-16 13:05:24Z
From: kmeaw Date: 2022-11-17 19:42:32Z > Хороший разработчик и исключения будет проверять и ошибки. Безусловно, на Go можно писать хорошо и избегать всех описанных мной проблем. Но если я буду наугад брать чужие библиотеки, которые по описанию кажутся мне полезными, то далеко не каждая из них будет написана хорошо, особенно если есть транзитивные зависимости. Фундаментальная разница с исключениями в том, что для того, чтобы получить хороший результат с ошибками, надо сознательно написать код, который внутрь ошибки затащит контекстуальную информацию. А для аналогичного результата с исключениями не надо вообще никакого кода писать. func f() (*Result, error) { r1, err := g1() if err != nil { return nil, fmt.Errorf("cannot g1: %w", err) } r2, err := g2(r1) if err != nil { return nil, fmt.Errorf("cannot g2: %w", err) } return r2, nil } func main() { res, err := f() if err != nil { log.Fatalf("cannot f(): %s", err) } } может написать "cannot f(): cannot g2: unexpected EOF" против (на несуществующем диалекте go-with-exceptions) func f() *Result { return g2(g1()) } func main() { res := f() } может написать: uncaught exception: unexpected EOF goroutine 1 [running]: main.g2(0xfee1dead) /go/src/foo/g2.go:11 main.main() /go/src/foo/main.go:22 И сразу понятна причина.
From: Sergey Matveev Date: 2022-11-17 19:56:31Z
From: Sergey Matveev Date: 2022-11-17 20:00:39Z Плюс мне кажется что авторы библиотек априори всё же немного более квалифицированы, чем те, кто просто на основе рецептов со stackoverflow что-то клепают. Сделать "package main" программу -- это одно. А сделать нечто что может использоваться как библиотека -- уже чуть более сложнее сделать, автоматом означая и чуть-чуть более продвинутый уровень разработчика. Поэтому помогать и подталкивать к нужным действиям (проверять и проверять ошибки) нужно наименее "квалифицированных"/опытных. -- Sergey Matveev (http://www.stargrave.org/) OpenPGP: 12AD 3268 9C66 0D42 6967 FD75 CB82 0563 2107 AD8A
Сгенерирован: SGBlog 0.34.0