Pull to refresh

Comments 287

"у меня возникло пару мыслей"… ндэ...

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

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


и вежливое указание на опечатку в приватном сообщении.

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

природа методов в Go означает, что функции тоже могут иметь методы

Что это значит? Не структуры ли данных могут иметь методы?
и интернет благодаря ним стал лучше

им.
A type may have a method set associated with it.

И каким образом это соответствует вашему
функции тоже могут иметь методы

?
Это как раз соответствует моему
структуры данных могут иметь методы
И каким образом это соответствует вашему. Это как раз соответствует моему

В Go есть не только структуры, но и другие типы — базовые типы, массивы, слайсы, функции, мапы(map), указатели, каналы. На любой из них можно повесить свой метод (через type MyType map/chan/func.... понятное дело).

В Go есть не только структуры

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

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

Не очень понимаю, что вы под этим понимаете. Куда передаётся?


Если вы пишете:


type MyType func() bool

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

Да, верно, но это не функция. Это, как раньше называли, функциональный тип данных.
Функция — это коробка, которая выполняет работу, она не является типом данных. Так же, как не является т.д., скажем, оператор.
Но вы можете создать тип данных, который указывает на функцию с вот такой-то сигнатурой, что и сделано строкой
type MyType func() bool

ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D1%82%D0%B8%D0%BF

А с сигнатурой


type MyType string

тип указывает на строку в вашей терминологии?


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

А с сигнатурой

Слово «сигнатура» мною было применено к функции, «функция с такой-то сигнатурой» — т.е. набором параметров и типом (пами) возвращаемых значений.
тип указывает на строку в вашей терминологии?

Вы путаете тип данных и конкретное значение. Функция — это значение.
type MyType string
описывает тип данных «строка», и переменная такого типа может принимать разные значения — конкретные строки.
type MyType func() bool
описывает функциональный тип данных, и переменная такого типа может принимать разные значения — конкретные функции (технически — ссылки на функции, но это только деталь реализации) с вот такой вот сигнатурой.

Мне кажется, вы вместо того, чтобы прочесть спецификацию и понять, как устроены типы в Go, пытаетесь объяснить это своими словами, какими-то удобными концепциями из вашего предыдущего опыта. Отсюда и путаница про «указатели», «ссылки» и «коробки, выполняющие работу».

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

Нет, не путаю.


технически — ссылки на функции, но это только деталь реализации

Откуда вы это взяли?


Почему своими? Есть устоявшаяся терминология, ссылку на википедию с описанием см. выше.

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

Правильная картинка вот :)

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

Я реально слабо представляю себе кейс когда человек решит перейти с C#/Java/D/Swift на Go и сможет внятно объяснить свое решение.

Особенно занятно читать вранье про простоту Go. Каким местом он простой? Что на нем делается проще чем на других языках? Такой бред могут нести только люди никогда в жизни ничего серьезного не писавшие. Вся эта простота (а по факту примитивность) приводит лишь к тому что на мало-мальски сложных проектах начинается дикое костылестроение т.к. в языке не оказывается каких-то элементарных синтаксических конструкций. Итогом является закономерный Го-внокод.

я заметил интересную особенность, что на Go активно переходят только PHP-шнки

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


Я реально слабо представляю себе кейс когда человек решит перейти с C#/Java/D/Swift

Вы бы ещё R или Julia назвали. По моим наблюдениям, чаще переходят с C/C++, Ruby/Node.js/Python/PHP.


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

Слово "простота" очень легко трактуется с точностью до наоборот разными людьми. Тут глупо спорить. Кто-то считает, что если учить 4 года Haskell, то потом любая программа помещается в 3 строчки и это "простота языка", но с инженерной точки зрения простота это противоположность сложности. Почитайте "Мифический человеко-месяц" Брукса, ну, или хотя бы его же "Серебрянной пули нет", там хорошо про сложность разложено.

>Тут глупо спорить. Кто-то считает, что если учить 4 года Haskell
Так, стоп. Причем тут Haskell. Вопрос был в том чем Go проще C#/Java/D/Swift/Python. Можете пример показать? Ну вот допустим я в Python могу проверить вхождение так:
if x in arr
На D это будет:
if (arr.canFind(x))
На других языках тоже примерно так же.

Вы будете утверждать, что на Go это проще делается?

По факту больше кода, больше ошибок, дольше разработка, сложнее рефакторинг, тестирование, отладка и тд.

Так ГДЕ Go простой??

Вы путаете простоту и количество символов, и на этом строите свои представления о том, как происходит рефакторинг и тестирование в Go.


Так ГДЕ Go простой??

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

>Везде
Да ладно врать то. Вы примеры покажите чем Go проще других языков.

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

>и угадать, что же он попытался упаковать в те 5 символов
Ага, к примеру по `canFind` или `Сontains` ну совершенно не очевидно что они делают и приходится гадать.
Вы примеры покажите чем Go проще других языков.

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


package main

import (
    "fmt"
    "time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        time.Sleep(time.Second)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 1; a <= 5; a++ {
        <-results
    }
}

Поверьте, как только возникнет более или менее серьезная задача

Я пишу "более или менее серьезные задачи" на Go уже больше 4 лет, и общаюсь с огромным количеством Go программистов по всему свету, включая ребят из Google, Uber, Dropbox, Cloudflare и Docker. Мои наблюдения очень отличаются от ваших. Расскажите, пожалуйста, сколько "более или менее серьезных задач" на Go вы написали.

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

Простите, но IMHO это лукавство — вы приводите пример не простой задачи, а той, на которой Go специализируется. Давайте приведем действительно простую задачу, и посмотрим насколько проще этот код будет выглядеть на Go:
echo Hello>out.txt
package main

import (
    "io/ioutil"
)

func main() {
    ioutil.WriteFile("out.txt", []byte("Hello"), 0664)
}
но IMHO это лукавство — вы приводите пример не простой задачи, а той, на которой Go специализируется.

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

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

А как насчет абстрактного бинарного дерева?)

Вот вам пример на python:


from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    with Pool(5) as p:
        print(p.map(f, [1, 2, 3]))

Куда проще?

Хаха, вы опять пришли потроллить? Код который вы написали (или скопировали из первого результата гугла, как вы это любите делать)
а) запускает 5(!) разных процессов
б) не совсем тот же, код который просили написать
в) не запускается :)


$ python main.py
Traceback (most recent call last):
  File "main.py", line 7, in <module>
    with Pool(5) as p:
AttributeError: __exit__
a) И? У вас были условия на количество процессов?
б) «Вот держите примитивный пул воркеров» — это примитивный пул воркеров. Нет?
в) Потому что я скопировал этот пример из третьего питона, попробуйте на нем.

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

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


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

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

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


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


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


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

Согласен. Поинт был в том, что горутины и OS процессы это разные лиги вообще.

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

Внезапно, но нет. Просто программа упадет с ошибкой.


Слишком много процессов
Traceback (most recent call last):
  File "test.py", line 7, in <module>
    with Pool(10000) as p:
  File "/usr/lib/python3.6/multiprocessing/context.py", line 119, in Pool
    context=self.get_context())
  File "/usr/lib/python3.6/multiprocessing/pool.py", line 174, in __init__
    self._repopulate_pool()
  File "/usr/lib/python3.6/multiprocessing/pool.py", line 239, in _repopulate_pool
    w.start()
  File "/usr/lib/python3.6/multiprocessing/process.py", line 105, in start
    self._popen = self._Popen(self)
  File "/usr/lib/python3.6/multiprocessing/context.py", line 277, in _Popen
    return Popen(process_obj)
  File "/usr/lib/python3.6/multiprocessing/popen_fork.py", line 20, in __init__
    self._launch(process_obj)
  File "/usr/lib/python3.6/multiprocessing/popen_fork.py", line 66, in _launch
    parent_r, child_w = os.pipe()
OSError: [Errno 24] Too many open files

А уж если вам настолько сильно нужны корутины, то можете запустить вот это (только вам нужно будет установить библиотеку gevent).


from gevent.pool import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    pool_1 = Pool(10000)
    print(pool_1.map(f, list(range(0, 10000))))

Надеюсь, вас не смущает в этом плане внешняя зависимость, ведь для golang очень часто приходится добавлять внешнею зависимость для обработки ошибок от dropbox.

Просто программа упадет с ошибкой.

Удивительно эффективное решение вы написали.


ведь для golang очень часто приходится добавлять внешнею зависимость для обработки ошибок от dropbox.

Вы себе сами придумываете эти фантазии или кто-то помогает?

> Вы себе сами придумываете эти фантазии или кто-то помогает?

Ну вот kubernetes и moby используют. traefik использует. А часть других проектов типа gin пилит для этого свои костыли. Некоторые обходятся без этого, но не все.

> Удивительно эффективное решение вы написали.

В первоначальном задании не было ничего о «а запустить сколько угодно воркеров». Какое тз — такое и решение.
Т.е. эта портянка и есть тот самый Go?
:) так а в чем профит-то?
Новый язык можно создавать по нескольким причинам:
1) just for fan! ну типа я такой крутой и вот что я могу!
2) вынужденная необходимость и отсутствие альтернатив
3) улучшить существующее положение дел
Чем вот это:
for j := 1; j <= 5; j++ {

Отличается от Сишного:
for( j = 1;j <= 5; j++) {

?
Или вот это:
func worker(id int, jobs <-chan int, results chan< — int)

от Сишного:
void worker(int id, const int *jobs, int *results)

Тут возможно что-то не так с jobs и results, т.к. мутки со стрелочками не совсем понятны, хотя названия переменных наводят на мысли что jobs это входные данные, results — выходные.
мутки со стрелочками не совсем понятны

Дайте я вам плюсов в карму подкину, это прекрасно :D


Чем вот это Отличается от Сишного:

В этом была и идея, что язык не должен радикально отличаться от известной базы, чтобы облегчить вход. Большинство людей, знакомых с С, осваивают Go за одни выходные, и это big deal.

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

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

Извиняю, но я даже не знаю, как можно не видеть общего между ними. Боюсь, я тут не помогу в таком случае.

Ну а что там вы видите общего?
Нет, на полном серьезе. Хочу вас понять. Что там общего с C, кроме фигурных скобок в «statement block» (это сейчас-то и к наследию C не отнесешь).

То, что я вижу в Go vs C:

  • есть интерфейсы;
  • декларация переменной — порядок имени и типа противоположный;
  • инклудов, макросов нет;
  • синтаксис control statements другой;
  • ...


Что общего с C/C++ вы нашли в Go?
Вот, кстати, тут человек не видит различий между C и Go вообще

Там речь не о различиях вообще, а о том, что Go проще, чем C.

Думаю там видно, что:
func worker(id int, jobs <-chan int, results chan<- int)

отличается от:
void worker(int id, const int *jobs, int *results)

и «сишник» этот код на Go не поймет и будет спотыкаться при его написании (хотя бы из-за смены порядка указания имя/тип).
… с тем же успехом я могу написать код на паскале:
function worker(id : integer; jobs : intchan; results : intchan) : boolean;

и сказать, что Go произошел от Delphi.
Вы троллите, да?

Ответ: «и C, и Go являются тьюринг-полными языками программирования», конечно, правильный, но я хотел бы услышать что-то более конкретное.
Для сравнения, вот аналогичный код на C# под .NET Core 1.0

using System;
using System.Linq;
using System.Threading;

namespace Go_comparison
{
    class Program
    {
        static void Main(string[] args)
        {
            var jobs = Enumerable.Range(1, 5);

            jobs.AsParallel().WithDegreeOfParallelism(3)
                .Select(x =>
                {
                    Console.WriteLine($"Starting work on iten {x}");
                    Thread.Sleep(1000);
                    Console.WriteLine($"Finished work on iten {x}");

                    return x * 2;
                })
                .ForAll(Console.WriteLine);

            Console.ReadLine();
        }
    }
}
Справедливости ради нужно отметить, что результатом на го будет бинарник в 1 мб, а в шарпе — папка с кучей дллок фреймворка на 45 мб (если мы хотим получить идентичный результат, «без зависимостей»). Сам код на C# безусловно восхитительно выглядит.

На Elixir:


1..10000 
|> Enum.map(&Task.async(fn ->
  IO.puts("worker #{&1} started job")
  :timer.sleep(1000)
  IO.puts("worker #{&1} finished job")
end))
|> Enum.map(&Task.await/1)
Если ваш 'arr' это мэп то так:
if _, ok := arr[x]; ok {...}

если же у вас там массив то в цикле проверять придется.
В общем так же как в плюсах.
Так, тоесть вы уверяете, что запись вида:
if _, ok := arr[x]; ok {...}

Читается лучше чем к примеру:
if (arr.canFind(x))

>если же у вас там массив то в цикле проверять придется.
А ну вообще прелестно! Проще некуда!

Вот видите даже в каких-то стерильных условиях типа: «Если ваш 'arr' это мэп то» код на Go оказывается куда менее читабельный чем на других языках.
Я пришел на го с плюсов. На плюсах проверить входит ли значение в мэп приходится вот так
if (arr.end() != arr.find(x)) { ... }

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

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

Ну и у меня встречный вопрос — как вы полагаете может быть сделана проверка вхождения элемента в контейнер без учета типа контейнера. Учтите, что массив не являетеся классом, это просто указатель, из него нельзя вызывать функций.
Ну если контейнер будет являться диапазоном (Range) то canFind будет с ним так же работать. Если нужно что-то другое, то ничего не мешает реализовать у контейнера свой метод Contains.

Но тут то вся соль в том, что на Go нужно костыли городить уже в простых случаях.
Не совсем корректный пример, Ваш код на плюсах делает нечто большее. Просто проверка вхождения делается вот так:
if (map.count(key) > 0) { ;; }

find имеет смысл если сохранять итератор и потом его использовать для доступа к элементу — экономится один поиск по дереву. То есть это дополнительная функциональность С++, которая не всегда легко доступна в других языках, но пользоваться её не обязательно и не всегда имеет смысл.

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

Да, я думаю, что между "выразительностью" и "понятностью, что автор имел ввиду", у Go приоритет смещён на последнее.

Мне кажется, это скорее не простота, а компактность. И тут палка о двух концах. Если число концепций слишком небольшое, то не остается ни одного очевидного/хорошего способа сделать что-то хоть немного нетривиальное. Зато, конечно, есть куча сомнительных способов и вся простота компактного языка улетучивается. Даже такой некомпактный язык как С++ и то временами недостаточно гибок — в новых стандартах приходится новые возможности вводить и всё ещё есть что добавить (мультиметоды к примеру).

Нет. Знаю порядка сотни go разработчиков. Php, C — чуть ли не в равной доле. Много perl ребят нашло себя в go. Также не редкость java, node.js, c#.

Я пришел из с/с++ в go. И жалею только о том, что его ещё нет в микроконтроллерах. И наверное не скоро будет. Слишком мало памяти в контроллерах.
Пишем уже два года. И переходить назад вообще не хочется. Проект, кроссплатформенный. В том числе и на мобильных устройствах.
Раздражает только один момент. Паника при выходе за массив или слайс. Меня бы устроила просто обработка ошибки. Сейчас приходится проверки ставить перед каждым обращением.
Особенно занятно читать вранье про простоту Go. Каким местом он простой? Что на нем делается проще чем на других языках? Такой бред могут нести только люди никогда в жизни ничего серьезного не писавшие. Вся эта простота (а по факту примитивность) приводит лишь к тому что на мало-мальски сложных проектах начинается дикое костылестроение т.к. в языке не оказывается каких-то элементарных синтаксических конструкций. Итогом является закономерный Го-внокод.

Потому что когда кое-кто переходил с зоопарка C/C++ кода с кучей систем сборок, синтаксисов, редакций и прочего на милый уютный Golang, количество синтаксического сахара к котором стремится к нулю, люди возрадовались, потому что вдоволь наелись проблем, которые порождает слишком гибкий язык. Другое дело, что потом они рано или поздно осознают к тому, что golang порождает еще больше проблем из-за отсутствия значительных частей этого синтаксического сахара в виде кучи кривых конструкций и просто синтаксического мусора в коде в духе if err!=nil — прокинуть и поймут, что не зря раньше от этого отказались, и займутся всерьез внутренними соглашениями по коду вместо беготни по языкам.

Можно узнать чем именно плох if err!=nil? И почему отказались. Может в моих программа структура кода какая то выраженная, но мне наоборот удобно использование этой конструкции. Замусоривания я никакого не вижу. Ничего не пропустишь (конечно же если не ставить везде "_" ).
Почему нельзя сделать ее автоматической, как это сделали в пробросе ошибок в других языках?
Опять же, если вам нужна более строгая система ошибок, то ничего не мешает сделать так, как это сделали в Java, что бы нужно было или указывать у метода throws с типами ошибок, которые он может возвращать или же обрабатывать их в методе.
Если вас беспокоит оверхед от сохранения стека вызова — не сохраняйте его по умолчанию и дайте пользователям возможность его включать там, где нужно.

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

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

То есть переложили с компилятора на программиста…

Не совсем. В Google в целом пришли к выводу, что польза от эксепшенов меньше, чем проблемы, которые они приносят. https://google.github.io/styleguide/cppguide.html#Exceptions
А в купе с идеологией языка — просто, но не проще, чем необходимо, это удачно сочиталось, да.

Извините, вы сами читали, что по вашей ссылке написано? Там как раз написано что


On their face, the benefits of using exceptions outweigh the costs, especially in new projects.

Пожалуйста, прекратите ссылаться на Google Style Guide как аргумент против исключений.

Извиняю, конечно читал. Я указал на этот документ не как "аргумент против исключений" (там есть аргументы и за, и против), а как аргумент того, что в Google не используют эксепшены, что, естественно, повлияло на выбор способ обработки ошибок в Go. Тоесть "сложность адекватной реализации" не была первопричиной. Главными причинами являются как раз те моменты, которые описаны в Cons в Google Style Guide — и они в идеологии языка Go имели больший вес.

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

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

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

Нет, но те минусы исключений, которые упоминаются в Go FAQ не имеют отношения к конкретной реализации в плюсах, а к самой концепции в целом.

Нет, но те минусы исключений, которые упоминаются в Go FAQ

Но ссылку вы дали не на Go FAQ, а на руководству по стилю для плюсов, где исключение и спроектированы, и реализованы, и применялись криво.
В Go есть полный эквивалент throw-catch-finally в виде panic-recover-defer. Так что концепция в целом никуда не делась, несмотря на всю критику.
Разве что про вышеупомянутую эквивалентность в блоге Golang деликатно не упоминается.

Ещё раз — ссылка была дана, чтобы показать, что некая культура "ухода от эксепшенов" уже была в Google ещё до Go. И те проблемы, который озвучены в Go FAQ как раз не относятся к реализации С++, и не касаются panic-recover-defer.


В частности, главный аргумент это все таки то, что эксепшены стимулируют:
а) неправильно их использовать (panic/recover не используют для события "юзер не найден", это надо прям совсем удариться, чтобы так использовать)
б) отношения к обычным ошибкам (которые обязательно произойдут), как неважному коду, который нужно спрятать подальше, чтобы он не "захламлял" хороший код.
в) уменьшают понятность error-path кода (понятность что происходит, без прыгания по другим файлам и функциям)


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

И те проблемы, который озвучены в Go FAQ как раз не относятся к реализации С++, и не касаются panic-recover-defer.

В чем принципиальная разница между throw-catch-finally и panic-recover-defer?
Почему проблемы первого варианта внезапно перестали касаться второго?


эксепшены стимулируют:
а) неправильно их использовать (panic/recover не используют для события "юзер не найден", это надо прям совсем удариться, чтобы так использовать)

Не понимаю. Что именно в исключениях стимулирует ошибки, а в панике — нет?


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

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


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

И здесь у паники никакого различия с исключениями не видно, по крайней мере лично мне.


Я лишь делаю акцент на том, что первопричина отсутствия эксепшенов в Go это не "сложность реализации", а более практические соображения.

А я делаю акцент на то, что исключения в Go есть, просто их зачем-то переименовали в panic, после чего формально верно заявили что никаких исключений у нас нет.

В чем принципиальная разница между throw-catch-finally и panic-recover-defer?

В том что panic-recover это дополнительный способ обработки действительно критических ситуаций, а throw-catch-finally — основной и единственный.


Что именно в исключениях стимулирует ошибки, а в панике — нет?

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


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

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


А я делаю акцент на то, что исключения в Go есть, просто их зачем-то переименовали в panic

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

В том что panic-recover это дополнительный способ обработки действительно критических ситуаций, а throw-catch-finally — основной и единственный.

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


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

"Более простой и удобный способ" с кодами ошибок и так поддерживается абсолютно везде. Это в общем-то исторически первый способ.


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

Если надо именно падать — никакой recover не нужен. А так то же самое исключение вид сбоку — раскрутка стека с возможностью перехвата на более высоком уровне.


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

Вы не могли указать, у кого конкретно "для обработки ошибок есть только исключения"?


Паника это как раз "исключения здорового человека".

То есть все разговоры про "в Go нет исключений" оказались чистым маркетингом, построенном на переименовании?

Неправда

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


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

В Go нет кодов ошибок.


Если надо именно падать — никакой recover не нужен.

Есть ситуации, когда нужен. А так да, panic() чаще используется без recover().


То есть все разговоры про "в Go нет исключений" оказались чистым маркетингом, построенном на переименовании?

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

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

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


Мало того, на хабре есть и другие иллюстрации:
https://habrahabr.ru/post/118898/


Для реального примера использования panic и recover смотри пакет json из стандартной библиотеки Go. Он декодирует закодированные в JSON данные с помощью набора рекурсивных функций. Когда на вход поступает неправильно сформированный JSON, парсер вызывает panic, чтобы развернуть стек к верхнему вызову, который восстанавливается после паники и возвращает подходящий код ошибки (смотри функции «error» и «unmarshal» в decode.go). Похожий пример такой техники в процедуре Compile пакета regexp. Существует соглашение, что в библиотеках Go, даже если пакет использует panic внутри, его внешнее API возвращает явные коды ошибок.

В Go нет кодов ошибок.

Судя по цитате выше, другие гоферы об этом не в курсе.


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

Не понял. Вы сначала сами признали что panic — это исключения. А теперь сами же это отрицаете. Взаимоисключающие параграфы.

Так и в го есть два способа обработки ошибок

Об этом я вам и говорил. Вы сами с собой спорите уже.


Судя по цитате выше, другие гоферы об этом не в курсе.

Вы сейчас используете чьи-то посты на хабре, чтобы продолжать называть вещи неверными именами? Окей, продолжайте называть как хотите.


Не понял. Вы сначала сами признали что panic — это исключения. А теперь сами же это отрицаете

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

Окей, продолжайте называть как хотите.

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


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

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

"Более простой и удобный способ" с кодами ошибок и так поддерживается абсолютно везде.

Не совсем так. Конечно в Java вы тоже можете написать библиотеку, которая вместо FileNotFound будет возвращать некий код (как в C), но так не делают, ибо так не принято.


Отсюда растут две проблемы.


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

А вот в Go не принято кидать panic из библиотеки. Считается, что паниковать имеет права только ваше приложение. Т.е. эксепшену отведена совсем другая роль.


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

так не принято

"Так не принято" — это соглашение, а не язык, как меня здесь упорно убеждал автор статьи.
В .NET для API библиотек принято иметь оба варианта. Приложение выбирает удобный для своей семантики.
Но сам язык опять-таки ничего в этом плане не навязывает.

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

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

В .NET для API библиотек принято иметь оба варианта.

А как это реализуется? В смысле выбор.

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


  1. Метод Parse бросит исключение,
  2. Метод TryParse вернет флаг как результат (и распарсенное как out-параметр при успехе)
  3. Метод ParseOrDefault вернет null

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

Конечно в Java вы тоже можете написать библиотеку, которая вместо FileNotFound будет возвращать некий код (как в C), но так не делают, ибо так не принято.

Да ладно!

Вот, например: «boolean File.delete()», «boolean File.mkdir​()» и т.п.

Ну ок, не знал. Много таких библиотек в общей массе?

Довольно много размазано по различным кускам стандартной библиотеки. Та же банальная работа с коллекциями часто имеет подобные методы для частых случаев (например, j.u.Map#get(key)). Инстанцирование exception'а достаточно дорогая операция из-за сбора стектрейса, если специально не выкидывать его сбор. И, соответственно, exception'ы, как правило, не используются для control flow.

Не совсем. В Google в целом пришли к выводу, что польза от эксепшенов меньше, чем проблемы, которые они приносят.
Ну вот зачем вы перевираете. Отказались исключительно в «C++».
Гляньте на гитхабе в публичные гугловые исходники для Java… Посчитайте сколько там исключений… Инересно, почему от них не отказались?
Или посмотрите на проекты на Python — тоже исключения повсеместно (в том числе кастомные).
Я не настаиваю, но думаю оверхед там не только из-за сохранения в стеке, но и само выполнение экепшеена требует больше ресурсов чем обработка одного if. В много-поточной системе, это всё выйдет с ещё с большей потерей ресурсов.
Ещё эта конструкция мне удобна тем, что если нужно обработать ошибку в зависимости от кода ошибки, то я это делаю сразу в нужном месте. И если нужно передать ошибку наверх то могу добавить что то к уже имеющемуся коду. Например какая именно функция выше уровнем не смогла открыть файл. Конечно же это можно сделать и с использованием эксепшена. Но это будет как раз замусоривание кода.

Я не говорю, что вот "впилите экспешены, вот что бы прямо как в java", но почему прокидывание err наверх нельзя было сделать поведением по умолчанию, если другое не указано?


Например, что бы можно было бы написать


func t1(x int, y int) (int, error){
    r1 = t2(x,y) // неявная ошибка
    return r1;
}

вместо


func t1(x int, y int) (int, error){
    r1, err = t2(x,y)
    if err != nil {
        return nil, err
    }
    return r1, nil;
}
наверное так будет правильнее
func t1(x int, y int) (r1 int, err, error){
    r1, err = t2(x,y)
    if err != nil {
        return
    }
    return 
}

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

А если нужно сделать так?

func t1(x int, y int) (r1 int, err, error){
    r1, err = t2(x,y)
    if err != nil {
        return errors.New("Имеенно это не могло вызвать t2 при причине:",err )
    }
    return 
}
}

или так
func t1(x int, y int) (err, error){
    r1, err = t2(x,y)
    if err != nil {
        r3, err = t3(x,y)
        if err != nil {
            return Err
        }
        //  Gthtl тем как вернуться на верх  сделать ещё несколько проверок и вызовов.
    }
    return 
}
}
> почему прокидывание err наверх нельзя было сделать поведением по умолчанию, если другое не указано

То есть, если обработки нет и функция возвращает error, то прокидывать вставлять инструкции, которые будет прокидывать ошибку наверх. А если обработка есть, то, следовательно, поведение по умолчанию не нужно. Я думал над таким сценарием и мне он кажется в целом весьма нормальным. Возможно, я упустил какие-то возможные проблемы?

У такого решения есть два недостатка:


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

Согласен, но за это вы получаете:


  1. Понятность (особенно другими людьми, читающими ваш код), что происходит в случае ошибки
  2. Отношение к error-path с таким же уважением, как и к happy-path коду (в отличие от эксепшенов)
  3. Простоту и понятность обработки ошибки в программе в целом — это простая переменная: получил ее, обработал. Никакой магии и талмудов, о том как правильно пользоваться, никакого "да эти 78% программистов просто не научились правильно пользоваться эксепшенами".
Понятность (особенно другими людьми, читающими ваш код), что происходит в случае ошибки

Обычно происходит return nil, error что не добавляет ровно никакой полезной информации. Или ошибка вкладывается в _ и игнорируется — очень опасный вариант. Или ошибка не вкладывается в _ но все равно игнорируется — еще хуже.


Отношение к error-path с таким же уважением, как и к happy-path коду (в отличие от эксепшенов)

Целевой код решает поставленную задачу — без него вообще ничего не будет.
Код в error-path минимизирует убытки — это очень важная отдельная ответственность, ее логично и реализовать отдельно, без смешения и копипасты.


Простоту и понятность обработки ошибки в программе в целом — это простая переменная: получил ее, обработал.

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


Отказ от более сложных и мощных средств никак не уменьшает сложность задачи.


Как результат в Go есть механизм panic-recover-defer, делающий в точности то же самое, что и исключения. Следовательно, все равно придется решать, когда уместна паника, а когда код ошибки. Такие дела.

Обычно происходит return nil, error что не добавляет ровно никакой полезной информации.

Обычно добавляется полезная информация и ошибка заворачивается в что-то вроде return nil, fmt.Errorf("decode: %s", err) или заворачивается в свой тип, реализующий интерфейс error — return nil, DecodeError{line: line, err: err}, но во многих ситуациях достаточно и просто передать ошибку наверх — это нормально. Суть здесь в том, что ваш коворкер или вы через 3 месяца, читая этот код, мгновенно будете видеть, что происходит при возникновении ошибки, уменьшая таким образом количество догадок, ошибок в понимании workflow программы и подкапотной магии в целом.


Код в error-path минимизирует убытки — это очень важная отдельная ответственность, ее логично и реализовать отдельно

Ну это подход тех, кто привык к эксепшенам. Авторы Go же считают, что error path код, не нужно "реализовывать отдельно", и стимулируют к этому относиться как такому же важному коду, как и happy path. Написал if, напиши и else, другими словами. Вы можете быть с этим не согласны, но это тот подход, который выбран в Go и который, на самом деле, миллионы программистов считают более правильным и удобным.


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

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


Отказ от более сложных и мощных средств никак не уменьшает сложность задачи.

А оно и не должно. Сложность задачи (essential complexity) средствами не уменьшается, а вот выбор средств с меньшей сложностью (accidental complexity) это как раз то, к чему вся софтверно инжинерная индустрия и стремится (ну, не везде, правда))).


Следовательно, все равно придется решать, когда уместна паника, а когда код ошибки.
Именно, и паника очень редко когда на нужна по факту. И, к слову, вы написали "код ошибки", но коды ошибки это в С. В Go error не код, это интерфейсный тип, и это, мягко говоря, несравнимо.
Обычно добавляется полезная информация и ошибка заворачивается в что-то вроде return nil, fmt.Errorf("decode: %s", err) или заворачивается в свой тип, реализующий интерфейс error — return nil, DecodeError{line: line, err: err},

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


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

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


вторы Go же считают, что error path код, не нужно "реализовывать отдельно", и стимулируют к этому относиться как такому же важному коду, как и happy path.

Сможете показать, где это стимулируется средствами языка, а не просто руководством по стилю?
Error path НЕ такой же важный код как happy path по определению.
Без happy path у вас вообще ничего нет, без error path вы можете нести бОльшие убытки при нештатных ситуациях.
При использовании кодов ошибок их обработка перемешана с бархатным путем похлеще чем спагетти, занимает больше места (три строчки на одну), содержит массу копипасты, требует читать весь код при анализе ошибочной ситуации.


Сложность задачи (essential complexity) средствами не уменьшается, а вот выбор средств с меньшей сложностью (accidental complexity) это как раз то, к чему вся софтверно инжинерная индустрия и стремится (ну, не везде, правда))).

Задача обработки ошибок — это accidental complexity, никакой сущностной сложности в ней нет.
А вот happy path — это как раз essential complexity.
Исключения позволяют обработать ошибки концентрированно, централизовано, без копипасты, с максимально читаемым целевым кодом.


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

Да неужели? Если у вас случилась нештатная ситуация и она не совсем верно обрабатывается с помощью кодов ошибок, то вам придется


  1. Узнать о проблеме (необработанные коды ошибок молчат как партизаны)
  2. Предположить, где она возникла
  3. Пройти по всему стеку, проверив, как ее отрабатывает каждый метод в цепочке вызовов.
  4. Если предположение оказалось неверным вернуться к пункту 2

С исключениями:


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

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

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


Именно, и паника очень редко когда нужна по факту.

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


И, к слову, вы написали "код ошибки", но коды ошибки это в С. В Go error не код, это интерфейсный тип, и это, мягко говоря, несравнимо.

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

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

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


Error path НЕ такой же важный код как happy path по определению.

Это вы в это верите, и, почему-то считаете это какой-то абсолютной истиной. В Go это убеждения очень не приветствуется и язык вас заставит начать относиться к error-path как к такому же важному коду и сделает это новым определением.


требует читать весь код при анализе ошибочной ситуации.

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


Исключения позволяют обработать ошибки концентрированно, централизовано

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


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

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


Кстати, а когда нужна паника по вашему мнению?

Когда ошибка критична и программа (или модуль программы) не могут дальше продолжать из-за этой ошибки и должна прекратить выполнение no matter what.


"Код ошибки" — это роль, а не конкретный тип вроде целого числа.

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

Ни разу не полный. При явном возврате, вы тут же видите что происходит в error path. В случае исключений вам нужно скроллить вниз функции, держа в голове типы исключений.

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


Это вы в это верите, и, почему-то считаете это какой-то абсолютной истиной. В Go это убеждения очень не приветствуется и язык вас заставит начать относиться к error-path как к такому же важному коду и сделает это новым определением.

Вы только что сами утверждали, что язык ничего не заставляет. В нем же есть "исключения здорового человека" AKA panic.
Давайте уж определимся, кто именно заставляет — язык или руководство по стилю?


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

Не телепат и могу понять лишь то что написано. С исключениями для понимания читать придется меньше.


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

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


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

Может, но как и в любом варианте с кодами ошибок, такая информация должна добавляться вручную. Исключения дают все это из коробки автоматически.


Когда ошибка критична и программа (или модуль программы) не могут дальше продолжать из-за этой ошибки и должна прекратить выполнение no matter what.

И зачем тогда нужен recover, если no matter what?


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

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


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

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

Не вниз функции, а до ближайшей секции except.

А где обычно "ближайшая секция except"? Никогда не видел вверху функции или сразу под вызовом. Давайте по сути.


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

А вы специально продолжаете называть возврат ошибок в Go "кодами ошибок"?


Давайте уж определимся,

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


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

И понимать меньше, да.


Исключения дают все это из коробки автоматически.

И это ещё одна глупость — стектрейс далеко не во всех ошибках нужен. Но у вас же нет других способов, поэтому вы тратите память и такты процессора, чтобы гонять стекйтрейсы по поводу и без повода, даже не отдавая в этом отчет. Это не только лишний минус к производительности, но и просто неправильно. Почти каждая Python программа, например, вместо понятного сообщения для в консоль вроде "Ctrl-C pressed, aborting" или "File not found" вываливает стектрейс, но очень многие привыкшие к этому даже не понимают, почему это абсурдно. В Go вы получаете стектрейс только когда это надо.


И зачем тогда нужен recover, если no matter what?

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


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


Типичный пример — net/http библиотека и HandlerFunc.


Структурно рекомендуемый паттерн обработки ошибок в Go — это коды ошибок

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

А вы специально продолжаете называть возврат ошибок в Go "кодами ошибок"?

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


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

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


И понимать меньше, да.

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


И это ещё одна глупость — стектрейс далеко не во всех ошибках нужен.

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


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

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

И это ещё одна глупость — стектрейс далеко не во всех ошибках нужен.
Вы снова хамите. Зачем?

Вам кажется хамством утверждение, что не во всех ошибках нужен стектрейс? Я думал я уже ничему тут не удивляюсь :)


И попробуйте дать ответ на прямо поставленный вопрос.

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


Вы так и не дали подходящего общего термина

Дал, несмотря на то, что вы сами и не попытались взять. Хотите писать простыни комментариев про то как вас никто не заставит назвать вещи своими именами — пишите на здоровье. Мне это больше не интересно.

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

Просто "возврат ошибки", никаких кодов.

Обычно добавляется полезная информация и ошибка заворачивается в что-то вроде return nil, fmt.Errorf(«decode: %s», err) или заворачивается в свой тип, реализующий интерфейс error — return nil, DecodeError{line: line, err: err}

Очевидно, что полезность «return nil, fmt.Errorf('decode: %s', err)» стремится к нулю, т.к. эта ошибка для пользователя (причем, без учета i18n), а не программы, ее нельзя категоризировать и далее каким-либо осмысленным образом обработать.

А возвращение в стиле «return nil, DecodeError{line: line, err: err}», по-моему, упрется в то же, что и упираются исключения — лень программиста завести «DecodeError» и написать правильно.

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

Не передергивайте, речь шла про конструкцию return nil, fmt.Errorf('decode: %s', err) а не про обработку ошибок в общем.

Почему вы отвечаете за другого человека и объясняете мне, что вы считаете, что он имел ввиду? Куда вы дели из его ответа конструкцию return nil, DecodeError{line: line, err: err} и фантазии автора о том, во что оно упрётся?
Или вы тут один человек под разными аккаунтами?

Потому что мне есть что написать, потому и пишу. Кажется, правилами хабра это не запрещено.

Что же до строчки `return nil, DecodeError{line: line, err: err}` — то при чем тут она? Про полезность стремящуюся к нулю говорилось в контексте первой строчки, а не этой. Второй вариант как раз нормальный.

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

Я сейчас ничего доказать не пытаюсь. Я заметил что в споре был применен некорректный аргумент.
Мило. Вы это Go программистам пытаетесь доказать

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

Кроме этого, упомянутая проблема «стремления пользы к нулю» справедлива не только для Go, а вообще для любой системы, в которой будет использоваться подобная конструкция (строка ошибка). В C++ это будет:

throw «some error useless description»;.
упомянутая проблема «стремления пользы к нулю»

Я всё же позволю не согласиться с этим — во многих случаях как раз важно дать пользователю четкое описание того, что произошло, и строковое описание ошибки, дополненное на каждому уровне стека оказывается именно тем, что нужно. Напирмер, если пользователь запускает программу, и получает в ответ строку Can't load config: open ~/.config/myapp: insufficient permissions — это почти образец того, как подобная ошибка и должна быть обработана и подана юзеру. Тут не нужны стектрейсы. не нужна метаинформация — простая строка, объясняющая чуть возникшей ситуации, которая трактуется программой, как проблема.


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

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

Сообщения об ошибках, также как и любые сообщения, нужно отображать локализованными.

Не обязательно.


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

Элементарно позволяют.

И как же предполагается делать локализацию сообщения «Can't load config: open ~/.config/myapp: insufficient permissions»?

return nil, fmt.Errorf("%s: %s", i18n("decode"), err)
return nil, DecodeError{line: line, err: i18n(err)}


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

Вот после таких решений (я о первом) и появляются "локализованные" сообщения вида "Ошибка декодировать: открыть файл: нехватка разрешений".

Ещё раз, о чём вы спорите?
return nil, fmt.Errorf("%s: %s", i18n(«decode»), err)

Ха-ха-ха. Встречал я подобный код, только вместо i18n был стразу MessageBox. Чего мелочится-то? :-)

В 99% случаях это нарушение роли компонента в системе.
О чём вы сейчас спорите?
О чём вы сейчас спорите?

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

Ясно. А кто-то говорил, что они не полезны?

Да, это вы говорили:


Я всё же позволю не согласиться с этим — во многих случаях как раз важно дать пользователю четкое описание того, что произошло, и строковое описание ошибки, дополненное на каждому уровне стека оказывается именно тем, что нужно. Напирмер, если пользователь запускает программу, и получает в ответ строку Can't load config: open ~/.config/myapp: insufficient permissions — это почти образец того, как подобная ошибка и должна быть обработана и подана юзеру. Тут не нужны стектрейсы. не нужна метаинформация — простая строка, объясняющая чуть возникшей ситуации, которая трактуется программой, как проблема.
Да, это вы говорили:

В каком месте из приведенной цитаты следует, что "типизированные ошибки бесполезны"?

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

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

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

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

В таком случае логика нарушена у вас, вот в этом комментарии:


Ясно. А кто-то говорил, что они не полезны?

Потому что получается вот так:


Вы> A полезно
f> A бесполезно, нужно использовать B
Вы> разве я говорил что B бесполезно?

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

Я слушаю эти сказки про "удобство" и "в состоянии выучить" эксепшенов уже лет 10. Практика показывает, что подавляющее большинство кода на эксепшенах написано через одно место и создает много проблем. Последний раз, когда я спорил о пользе эксепшенов и слушал о том, что "это программисты были идиоты", спор закончился тем, что мне пришлось переводить сервис этого человека в докер-контейнер, и там было как раз важно ловить ошибки, что не так (порт не открыт например, или файл не найден или еще что) — и ни одна, ни одна из действительно ошибочных ситуаций не была корректно обработана. Эксепшены стимулировали "выбросил и словил наверху", такой подход в целом заставляет считать ошибки чем-то не особо важным, чем-то "а, потом займусь", и результат был на лицо. Спасло только использование strace. Спрашивается — зачем был вообще этот механизм ошибок "не для идиотов, а для элитных программистов, смотрящих свысока", который "очень удобный для использования", если всё равно толку от него ноль? Можно было просто делить на ноль и падать — было бы эффективнее. И я такого уже насмотрелся достаточно.
Более того, было тут исследование статическим анализом 400000 репозитороев на Java, в котором оказалось, что 78% проектов просто выбрасывают GeneralException и ловят его в самом верху. Ладно, вы можете не верить моему опыту и опыту сотен других, но с цифрами и статистикой сложно спорить. Поэтому лично я рад, что хотя бы в одном языке к вопросу подошли к практической и эмпирической точки зрения, а не с теоретических фантазий.


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

Я слушаю эти сказки про "удобство" и "в состоянии выучить"

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

У вас есть ссылки, сколько программистов неверно обрабатывали коды ошибок

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


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

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

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

У вас есть результаты статического анализа обработки ошибок на Go по большому набору проектов?

У вас есть результаты статического анализа обработки ошибок на Go по большому набору проектов?

Нет, но это кстати, одна из тем, которая мне интересна и я работаю над проектом, для статического анализа всей экосистемы open-source Go.

Нет, но это кстати, одна из тем, которая мне интересна и я работаю над проектом, для статического анализа всей экосистемы open-source Go.

Следовательно, вы не можете использовать статистику по исключениям явы как аргумент.
У вас просто нет статистики по го для сравнения.
Более того, у вас так же вообще нет статистики по обработке с помощью кодов ошибок (уж извините, но другого термина вы пока не предложили).
И результаты для явы очень легко объяснить: львиной доле программистов нужен результат, а не тщательная обработка ошибок.
Исключения тут просто индикатор, а не проблема сами по себе.


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

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

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

Как аргумент того, что эксепшены чаще используются некорректно — могу.


У вас просто нет статистики по го для сравнения.

У меня есть эмпирический опыт, да. Меня почему так порадовал тот paper по джаве, потому что мой опыт с эксепшенами примерно говорил тоже самое. Но опытом сильно не поаргументируешь, в отличие от цифр. Так что я был счастлив порадовать свой confirmation bias. С Go же я работаю уже 4 года и пока что вижу только то, что Go действительно заставляет людей начинать ценить ошибки, и код. где ошибки не еффективно обрабатываются, или уж тем более игнорируются — это редкость, которая долго не живет, особенно в опен-сорс мире. Можете меня ругать, что я недостаточно доступно могу объяснить почему и как Go это получилось достичь — я признаю, что сам могу не понимать всех механизмов — но всё таки, читая чужой код на Go я почти всегда вижу адекватную, понятную и правильную обработку ошибок.


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

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

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


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

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

Как аргумент того, что эксепшены чаще используются некорректно — могу.

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


У меня есть эмпирический опыт, да.

У меня тоже. Я к нему принципиально не апеллирую


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

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


я признаю, что сам могу не понимать всех механизмов

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


читая чужой код на Go я почти всегда вижу адекватную, понятную и правильную обработку ошибок.

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


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

Это вы про яву с ее checked exceptions? Серьезно?
Дело не в инструментах — в го есть полный аналог исключений.
И если им не злоупотребляют — дело не в языке, а в решаемых задачах.


в нем парсер AST и анализатор типов в стандартной библиотеке идут,

Для статистики по исключениям (или panic в го) с приемлемой точностью анализ по AST скорее всего не нужен от слова совсем. Такие дела.

но совершенно очевидно, что дело тут не в языке.

Совершенно очевидно, что именно в нём.


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

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


Для статистики по исключениям (или panic в го) с приемлемой точностью анализ по AST скорее всего не нужен от слова совсем.

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

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

Вы сами оформили всё своими словами про "идиотов" и "тем, кто хочет удобства и умным программистам не дают свободы" и в такой формулировке, конечно, это всех отталкивает.

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

Я не принимал ваше "идиотов" на свой счёт.


«пусть неудобно, зато никто не ошибётся»

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

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

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

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

Так это вы отвечали тем самым людям, что тысячи раз озвучивали этот аргумент или мне? Обычно если ваш комментарий под моим с отступом — это означает, что вы отвечаете мне, а я (если мне не изменяет память) ничего такого не писал. Я лишь сказал, что для меня это неудобно — про музыкальных кумиров и тупых разработчиков — это вы уже похоже сами додумали.
Точно 78%? По вашей ссылке на исследование не смог найти точный процент…
Боюсь представить эту вашу практику, которая показывает на какое-то одно место о котором все говорят, но никто не может объяснить конкретнее.
Ваша позиция по отношению к го мне примерно ясна из предыдущего диспута, можно было не писать ещё один такой длинный комментарий.

Это число из paper-а по исследованию Java exceptions. Последний раз когда я его находил (на Хабре в каждом посте про Go приходят комментаторы, рассказывающие про счастье эксепшенов, и дискуссия доходит до этого пейпера) — там была битая ссылка, но если хотите, могу найти оригинал, он у меня где-то есть.


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

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

Не трудитесь искать, я верю, что такой документ есть. Я даже верю, что есть такое исследование. Более того, я даже могу предложить, что действительно такой большой процент кода использует эксепшены как-то однобоко. Только что это доказывает? Лишь то, что видимо эксепшены удобно так использовать. Откуда уверенность, что это плохо конкретно в этом коде?
Вот подумайте сами, если бы го был мне неинтересен, зачем бы я стал заходить в эту статью? А я её прочёл да ещё и вместе с комментариями, видимо какой-то интерес есть? Проблемы только две. Мне кажутся некоторые решения этого языка немного странным и я пытаюсь понять, чем они обусловлены, но когда вижу такую аргументацию, то мне кажется, что я все же чего-то не понимаю. И второе, что вечно преследует го — это хейтеры. У языка похоже что-то не чисто с кармой, ему просто катастрофически не везёт с хейтерами, они наводняют любой пост и не дают любителям писать на го наслаждаться их любимым языком.
если бы го был мне неинтересен, зачем бы я стал заходить в эту статью?

Не принимайте на свой счёт, но тут большинство комментаторов — именно такие. Из года в год пишут одно и тоже, так и не удосужившись попробовать Go.


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

В Go масса моментов, которые разительно отличаются от "общепринятых" норм. Многие из них и для меня были явными WTF — как такое вообще можно было придумать. Но мне лично помогло то, что называется appeal to authority — которое часто является логической ошибкой, но не всегда. У меня было большое уважение к Кену Томпсону и Робу Пайку (надеюсь, не нужно рассказывать, кто это), и я просто доверился, что их практический опыт больше моего. Если они считают, что "неиспользуемый импорт это плохо и компилятор должен ругнуться", то возможно это имеет смысл, даже если мне это непривычно и всеми фибрами я этому сопротивляюсь. На удивление, очень быстро эти моменты стали нормой и сейчас я не понимаю, как можно было раньше жить без go fmt, с тоннами импортов, которые не используются, с классами, с миллионами способами сделать одно и то же и т.д.


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


В целом, основное отличие в Go в том, что они забили на новые веяния в PLT, поскольку видели, как все новые достижения приводили не только к прогрессу, но и к созданию нового класса проблем и сложности (эксепшены, наследование, shared memory concurrency и тд) и оставили в языке только минимум ортогональных концепций, которые себя зарекомендовали уже несколько десятилетий как.


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


Резюмируя — Go построен на практическом опыте того, что реально важно в real-world программировании, а не в академических исследованиях новых фишек.


И второе, что вечно преследует го — это хейтеры.

Это специфика рунета, в англоязычном интернете тоже троллей хватает, но там как-то поадекватней. Главное от чего такой хейт и пригорание — от того, что настолько "объективно примитивный язык" по их мнению, достиг таких высот. Особенно в этом плане показательно сообщество хаскеллистов — переводил как-то статью о том, где человек рассказывал о том, какой прекрасный Хаскель и какой ужасный Go, при этом признаваясь, что он может попросить коллегу выучить за неделю Go и закончить проект, а попросить выучить Хаскель и законтрибьютить не может, потому что у него у самого ушло 4 года чтобы выйти на какой-то достойный уровень, — и это да, решает.

Резюмируя — Go построен на практическом опыте того, что реально важно в real-world программировании, а не в академических исследованиях новых фишек.

Особенно важно для реального мира было сохранить ту самую "ошибку на миллиард долларов"?

Особенно важно для реального мира было сохранить ту самую "ошибку на миллиард долларов"?

В Go есть понятие zero value, и nil лишь один из частных его случаев. Но мне нравится, как вы пытаетесь всеми силами любые шаблоны и манипулятивные фразы привлечь, чтобы самому себе что-то доказать. Очень показательно для C# коммьюнити.

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

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

Да я-то вас понял.
Но должен же был кто-то в качестве жеста доброй воли объяснить эту автору статьи.

Автор статьи — Роб Пайк.


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

Это вы сами себе уже придумываете и сами же с этим боретесь. В Go как раз почти все концепции, включая ту же CSP модель concurrency — это то, что зарекомендовало себя десятилетиями.

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

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

А как вы замерили "на порядок лучше"? Как раз "практичность и эмпиричность" означает проверенность временем и обкатанность реальными программистами на реальных проектах, а не теоретические размышления, что будет лучше. Тоесть я не спорю, вполне может быть, так как вы написали, но ровно с такой же вероятностью может оказаться фантазией, разбившаяся о реалии человеческой психики. Я не сильно слежу за миром Rust, но из нескольких свежих статей сложилось впечатление, что Rust-программисты злоупотребляют .unwrap().


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

А как вы замерили "на порядок лучше"

На личном опыте написания программ на языках Rust и Go.


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

Вы хотите сказать, что программы на Rust не обкатаны на реальных проектах?


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

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

Вы хотите сказать, что программы на Rust не обкатаны на реальных проектах?

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


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

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

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

Вас похоже задело фраза «на порядок лучше».


Давайте я перефразирую.


В Rust, так же как и в Go, нет исключений. После написания программ на Go и Rust, я считаю (это ключевой момент, это моё мнение), что обработка ошибок в Rust сделана на порядок лучше.


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


При этом unwrap не рекомендуется для использования в продакшене, т.к. необработанная ошибка упадёт в рантайме с паникой. Использовать unwrap в Rust, равносильно пропуску обработки ошибки в Go.

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


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

Почему в го нет исключений?
panic-recover-defer полностью эквивалентен throw-catch-finall с точностью до синтаксического сахара.

Bonart вам уже в соседних ветках 100 раз объяснили. Хватит спамить.

Нет, ну если хотите, называйте panic-recover-defer исключениями :)


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


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


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


Или, например, чтобы отдать пользователю по HTTP вместо 200 статуса, какой-нибудь 400-тый выбрасывают исключение.


И у меня отвращение от такого использования исключений.


В Go для таких случаев не будут применять panic-recover-defer.

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

В 100-й раз вам пишу одно и то же, но у понятия "исключения" есть конкретный общепринятый смысл — это конкретный механизм языка для обработки ошибок. В Go есть panic() для быстрого падения, есть recover() для случаев, когда нужно словить паники из кода, который не контролируешь, и есть defer() для случаев, когда нужно выполнить код при выходе из скопа функции.


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

Вас похоже задело фраза «на порядок лучше».
Давайте я перефразирую.

я считаю (это ключевой момент, это моё мнение), что обработка ошибок в Rust сделана на порядок лучше.

Да вы мастер перефразирования.


Использовать unwrap в Rust, равносильно пропуску обработки ошибки в Go.

Это было бы супер, если бы оказалось правдой!

Да вы мастер перефразирования.

Я специально сделал упор что фраза «на порядок лучше» — это моё личное мнение, основанное на опыте.

Использовать unwrap в Rust, равносильно пропуску обработки ошибки в Go.

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

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


работа программы будет молча продолжена

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

Под равносильностью я имел в виду, то что писать заключения вроде «программисты на Rust злоупотребляют unwrap», равносильно тому что «программисты на Go злоупотребляют пропуском обработки ошибок»,

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


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

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

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

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


Помимо unwrap-а как минимум есть варианты использовать цепочки обработки результата

А создатели Rust понимают, к чему обычно приводит наличие нескольких способов сделать одно и тоже?

гораздо более серьезных проблем

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

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


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


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


из моего опыта, это бесценно.

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

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


  1. наличие обработки ошибок проверяется компилятором;
  2. есть сахар, с которым не надо разбавлять целевой код 1:3 шаблонным;
  3. даже для "быстро и грязно" есть готовый маркер, заметный за километр
А это надо еще и замерять?

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


В го… надо делать ревью кода

Ревью кода надо делать во всех языках.


В расте же

Это прекрасно, но Rust делался всё же более теоретиками, чем практиками, поэтому вполне ожидаемо получить это:


image


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

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

divan0 дайте, пожалуйста, ссылки на эти статьи, где «все просто используют unwrap». Я бы с удовольствием почитал.

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


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


Я на раст тоже поглядываю уже который год, равно как и на Swift, правда между тем, как я моргаю, версия языка обновляется с пятнадцатой на восемьдесят девятую, что слегка смущает. Ну и посты в стиле "я фанат раста, два месяца зубрил, но так и не въехал, и не смог написать относительно простую программу" как бы подтверждают моё убеждение, что простота языка это слишком важный фактор, на который в Rust забили полностью.


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

Это прекрасно, но Rust делался всё же более теоретиками, чем практиками, поэтому вполне ожидаемо получить это:

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

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


Просто это ж несравнимо — что Грейдон, что Нико — кто они такие по сути? Просто талантливые ребята, которые увлекались теорией языков программирования, любили хачить языки и компиляторы. Это прекрасно, только это нельзя даже близко поставить рядом с Робом Пайком и Кеном Томпсоном, которые с 80х написали такое количество разнообразного софта, и проработали в таком количестве команд, всех размером и сортов, что ну это просто разные лиги.


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


Long story short, "практики" дизайнившие Go сказали — "мы возьмем в язык то, что действительно работает из нашего практического опыта, что действительно важно в реальных процессах и командах", а "теоретики", дизайнившие большинство остальных языков (не только Rust, увы) звучат иначе — "мы возьмем, то что было, и переделаем по новому, добавим много совершенно новых подходов и фич и будет круто". И часто оно и бывает круто, пока не приходят реальные программисты, реальные проекты и процессы. И дело не в одном или двух проектах, а в действительно масштабном использовании, когда десятки и тысяч программистов пишут софт на этом языке — вот тогда обнаруживаются совершенно непредвиденные паттерны использования и проблемы. А сумашествие в виде "ничего, поломаем язык и пофиксим" только усугубляет ситуацию.


Вобщем, это если кратко про "теоретиков" vs "практиков".

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

Не знаю, насколько проект удался, но Firefox Quantum основанный на servo уже в бете и в ноябре планируется релиз.


https://blog.mozilla.org/blog/2017/09/26/firefox-quantum-beta-developer-edition/

Не знаю, насколько проект удался, но Firefox Quantum основанный на servo уже в бете и в ноябре планируется релиз.

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

Но за 5 лет

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


Слабенько тянет на "практику".

Складывается ощущение, что вы думаете, что на Rust написан только servo и что пишут на нём только авторы языка.

Складывается ощущение, что вы думаете

Нет, я отвечал на конкретное приведение в пример servo.


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

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

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

Языка для такого проекта не было (с проблемами С++ не захотели связываться) и именно с языка и начался проект.

Много раз видел на хедхантере вакансии Go/Python, причем Go/PHP — ни разу (может мало мониторил?). Складывается ощущение, что на Gо часто пишут проекты, где раньше был питон. Но это лишь наблюдение

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

Перешел на Go с С/С++, Java и Python (для новых проектов). Очень счастлив что это сделал, теперь трачу больше времени на написание кода приложений, чем на отладку и попытки понять почему у меня огнестрельная рана в ноге. Веб приложения не пишу, лапшу не пишу (по-крайней мере стараюсь не писать). Я что-то делаю не так?

Итогом является закономерный Го-внокод.

Мне кажется Вы приписываете языку то, что относится к самим программистам. Если программист пишет говнокод на PHP и JS, то переход на тот-же D это не исправит.
Мне кажется Вы приписываете языку то, что относится к самим программистам. Если программист пишет говнокод на PHP и JS, то переход на тот-же D это не исправит.

Ну вот вы это понимаете, divan0, судя по его статьям про великий Go, нет.

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

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


  • Вот эта фича в Go откровенно непродумана, потому что ....
  • Нет, она отличная, потому-что <субъективное суждение/ссылка на авторитет/придирка к какой-то мелочи в объяснении>
  • Но это же не так <пример/аргумент/опровержение>
  • <обвинение в троллинге/попытка принизить знания собеседника/попытка троллинга/еще больше придирок>

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


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


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


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


PS: Как это я могу дискредитировать сообщество языка, на котором уже года три не писал?)

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

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

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

Стоит заметить, что это вы, а не я, путаете ветки, не читайте ники и банально не способы вести несколько разговоров параллельно. Задумайтесь, а существуете ли вы?)

Мой любимый язык — C, на работе программирую на C++. Go первый язык за много лет, который меня реально «вштырил» и мне нравится на нём писать.
В голову сразу пришли Ryan Dahl и TJ Holowaychuk. Это совсем не php-шники, если что.
Не знаю, чем это `goa`, `gol`, `goc` так понравились «коммандеру».

Хотя...
В том же Plan9, мимо которого Rob Pike не прошёл мимо, какая-то странная манера именования прослеживается:
asm0 — MIPS
asm5 — ARM
asm6 — AMD64
asm9 — PPC64


У «go» как названия скорее кучу недостатков можно найти, а не «оправданий», начиная от того, что у поисковиков с «go» не так всё гладко (недавно ещё и «Android Go» появился, который никак с Go не связан), заканчивая тем, что это название уже было занято другим языком программирования.

«golang» в запросах и тегах помогает отбросить неоднозначность, но это костыль.

Всё же надеюсь, что некоторые элементы в Go 2 введут. Я бы лично не откзался от четырёх


  • Generics — дабы иметь инструменты переиспользования кода
  • Result-like обработка ошибок, дабы не иметь проблем с "вернули не-окей и забыли обработать", вкупе с кодом забитым
    val, err := func()
    if err != nil {
    return <default value>, err
    }
  • Non-nullable ссылки по умолчанию. Nil non-nil interfaces это честно говоря жёсткий косяк. Вопрос того, ссылки в Го или указатели, на самом деле дискуссионный.
  • Деструкторы в каком-то вменяемом виде. defer не очень способствует надёжному коду.
Можете поправить, если я не прав, но за деструкторами может захотеться «семантики перемещения» (move semantics).
По-моему опыт C++ и Rust показывает, что без этого не очень хорошо живётся.

Моё мнение не отражает мнение авторов/сообщества Go, но «Вирусные» возможности языков программирования выглядят менее вероятными кандидатами.

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

Я бы лично не откзался от четырёх

Вы, наверное, читали https://habrahabr.ru/post/333346/? Там важный момент в том, что каждый может написать доступно — как именно вы не смогли "переиспользовать код" и к чему это привело из-за "отсутствия дженериков" в Go, или как нынешняя обработка ошибок приводит к проблемам в коде, и авторы Go это гарантированно прочтут и будут лучше понимать, куда смотреть. Только, как вы понимаете, посты из серии "я так привык, мне так удобней" вряд ли какой-то эффект принесут.


Деструкторы в каком-то вменяемом виде. defer не очень способствует надёжному коду.

Может ещё конструкторы, ещё и с перегрузкой? :) Посмотрите на SetFinalizer(), но это для очень специфических случаев. Обычно если "хочется деструкторов", то это вы ещё не отвыкли от предыдущих языков, а не "defer не очень способствует надежному коду" )) Defer совершенно другую задачу решает.

Хорошо, давайте попробую привести примеры. Сам код, увы, сейчас мне недоступен.


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

Стейт-прокси над стейтлесс сервером. Часть, ответственная за кеширование, была отделена от других кусков кода. Снаружи пришлось "просовывать" объект в виде interface{} и лямбду, которая его кастила к правильному типу и дёргала. Да, это непривычно для меня — я отвык от тайп-кастов по поводу и без с тех пор как перестал писать на голом С. Более общий пример — в Го любая не-встроенная структура данных будет построена на interface{}. Опять тайп-касты. В Java такая ситуация была до 1.5, после чего дженерики, хоть и урезанные, таки ввели.


как нынешняя обработка ошибок приводит к проблемам в коде

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


val1, err := op1()
if err != nil { return nil, err; }
val2, err := val1.op2()
if err != nil { return nil, err; }
val3, err := val2.op3()
if err != nil { return nil, err; }

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


Для сравнения, на любом языке с исключениями:


return op1().op2().op3;

На Rust, который тоже работает через возвращаемые значения


op1()?.op2()?.op3()?

Может ещё конструкторы, ещё и с перегрузкой?

Вот кстати с отсутствием конструкторов как "специальных функций" я полностью согласен. Ловля нетривиальных проблем с порядком инициализации и "самоубийства" в С++ при исключении в конструкторе (а особенно в одном из инициализированных по умолчанию полей) — удовольствие ниже среднего. Перегрузка функций и методов по типу аргумента — дело скорее вкуса. Может быть удобна, но может быть и использована криво.


Обычно если "хочется деструкторов", то это вы ещё не отвыкли от предыдущих языков

Defer совершенно другую задачу решает.

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

в Го любая не-встроенная структура данных будет построена на interface{}.

Это не так. Есть масса примеров библиотек с generic-структурами, в которых используются специализированные интерфейсы (вроде Item, у которого есть один метод Less — для сравнения значений). Вы просто определеяете для своего типа этот метод и передаёте его в функции для этой структуры данных как интерфейс (не interface{})


Опять тайп-касты. В Java такая ситуация была до 1.5, после чего дженерики, хоть и урезанные, таки ввели.

В Джаве 1.5 тайп-касты же всё равно происходят, но автоматически, неявно, насколько я понимаю. Тоесть, понятно, что в паре с type erasure это выглядит безопасно, но если мы говорим о скорости исполнения, то ситуация такая же. Я не знаю насколько просто в Java померять потерю в скорости от этих кастов, но в Go это легко сделать и на небольших интерфейсах (большая часть интерфейсов в Go содержит 1-3 метода) это около 2 наносекунд.


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

Мне кажется, вы сильно переоцениваете роль компилятора :) С какой стати у вас должен быть сегфолт, если вы не проверили ошибку? Для компилятора это валидная ситуация — например вы конвертируете строку в число, и эти строки вы захардкодили, и вообще это одноразовый скрипт за 5 минут — в такой ситуации вполне допустимо не проверить ошибку, никакой паники или сегфолта тут не будет. Тоже самое с "почистить ресурс" — смотря что вы под этим подразумеваете. Память сборщик мусора почистит, понятное дело, но defer используется для того, чтобы выполнить какие-то действия при выходе из скопа (это не обязательно означает "конец жизни ресурса" — это конец скопа функции). Вы можете в дефере вывести что-то в лог, или запустить горутину, разлочить мьютекс или вообще что угодно сделать.

вроде Item, у которого есть один метод Less — для сравнения значений

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


В Джаве 1.5 тайп-касты же всё равно происходят, но автоматически, неявно, насколько я понимаю.

Да, происходят. На это ругается множество людей с самого выхода 1.5


Мне кажется, вы сильно переоцениваете роль компилятора :) С какой стати у вас должен быть сегфолт, если вы не проверили ошибку?

Как бы


val, err := someOp()
// здесь забыли вставить if err != nil { ... }, компилятор промолчит
val.field.fieldOp() // привет паника - или сегфолт, уже не помню

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

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

val, err := someOp()
// здесь забыли вставить if err != nil {… }, компилятор промолчит

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


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

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

Чтобы проигнорить ошибку, вам придется сознательно написать вместо err — _ что означает "я сознательно игнорирую это значение, потому что так надо".

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


И чем при выходе из скопа не устраивают defer тогда?

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

Кроме того, что последний раз, когда я имел дело с Go, компилятор никак не предупреждал, что я его пропустил. Или ситуация поменялась, и я напрасно ною?

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

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

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

Даже вот в таком случае?


val1, err := foo()
if err != nil { return nil, err; }
val2, err := bar(val1)
val2.field.fieldOp()
В добавок, если я забуду обработать ошибку, компилятор мне слова не скажет.

Полно статических анализаторов, которые нереально дополняют компилятор. Включая и этот момент — посмотрите https://github.com/alecthomas/gometalinter

И что в этом случае происходит с хваленой скоростью сборки?

Ну, запуск 20-30 линтеров требует времени. Но никто не заставляет запускать их на каждый коммит, так что обычный цикл код-тесты-… работает в соответствии с хвалёной скоростью сборки. А вот когда открывается PR и тесты запускаются в CI — там можно и gometalinter прогнать. Кроме того, можно управлять тем, какие линтеры будут запускаться, и ограничиться теми, которые отрабатывают быстро (у gometalinter есть ключик --fast специально для этого случая).

Деструкторы? Для каких задач? Если освобождение ресурсов, то финализаторы отлично справляются. Опробовано на конвертации изображений в highload.

То есть по другим трём пунктам вопросов нет? :)

Л — логика…
Я ответил только то, что я ответил.


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

Л — логика…

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


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

Боже, ну почему все так вцепились в слово "деструкторы"? Я уже в нескольких ветках написал, что было бы приятно иметь хотя бы ворнинг "здесь может утечь ресурс, поставь defer".

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

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

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

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

Автору спасибо за труды. Восхищаюсь Вашим терпением. Как Вы умудряетесь спокойно и конструктивно ставить на место орды воинствующих хейтеров и тролЕй — у меня не укладывается в голове такое самообладание. Пишите чаще!
Тут такое дело. Автор сам воинствующий фанатичный троль.

Ага, для вас любой, кто что-то пишет или переводит про Go — воинствующий фанатичный тролль.

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

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


Ну и да, Хабр это уже практически единственный ресурс, где я пересекаюсь с русскоязычным сообществом, и эта пропасть между уровнем общения в англо- и испаноязычной среде и русскоязычной меня каждый раз по новой удивляет. На один и тот же материал на английском я получу 20 благодарностей, 5 вежливых вопросов и 2 аккуратных приватных замечаний об опечатке, на русском — кучу минусов от хейтеров, оскорбительных замечаний об опечатках, оскорбления Go, его создателей и всего сообщества и 0 благодарностей. Если кто и поблагодарит, то его минусуют. Веселая, вобщем, тут мезозойская тусовка. :)

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

не особо принято честно говорить гадости.

Смеюсь вголос. Там вообще не принято говорить гадости. Принято говорить приятности, благодарить и поддерживать других.


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

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

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

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

Ни «спасибо», ни «вы ничего не знаете о ...» читать в комментариях на техническом ресурсе не интересно ни капли, в отличие от «оператор Х обладает сайдэффектом Y (из документации ссылка Z)».

Спасибо — стрелка вверх. Не интересно — иди дальше. Бесполезный/вводящий в заблуждение материал/совсем велосипед — стрелка вниз.
В комментарии технические вопросы-ответы по делу без перехода на личности вообще.

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

Я понимаю, о чём вы. У Go есть ряд недостатков, но это не те недостатки, о которых пишут хейтеры.


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


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


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


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


Вы можете принимать это или нет, но Go сделали таким. Если вам не подходит такой язык — просто не используйте его. Но воинственно доказывать гоферам, что они не понимают, какими должны быть "правильные" языки программирования — это глупость. Это как приехать в Мадрид и доказывать прохожим, как нужно правильно разговаривать на русском, а не на вот этом вот непонятном, полном багов языке.


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

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

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

«единственно правильная обработка ошибок — это эксепшены»
А почему в Go нету эксепшнов? Может…
google.github.io/styleguide/cppguide.html#Exceptions
Если кратенько:
On their face, the benefits of using exceptions outweigh the costs, especially in new projects. However, for existing code… because most existing C++ code at Google is not prepared to deal with exceptions...
А теперь вспомним кого планировалось переманивать на Go… Совпадение? :)

Но ведь ООП в Go вообще нету :)

В каком месте его там нету? :) Или вы тоже считаете, что ООП это классы?


Про дженерики вы передергиваете.

Отнюдь.


Если кратенько:

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

В каком месте его там нету? :) Или вы тоже считаете, что ООП это классы?
Покажите мне в Go наследование? Желательно еще научите как делать виртуальные методы ;)
Так то я не пытаюсь сказать, что отсутсвие в Go ООП делает это язык плохим, скорее наоборот.

Забавно, но я этот же документ использовал выше, чтобы показать, что в Google отказались от эксепшенов.
Отказались в C++ (судя по гайдлайнам), сказав при этом «ну с исключениями в целом получше… но legacy и все такое». У вас инфа оностительно остальных языков или вы просто придумываете находу?

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

За что же мне это. С чего вы взяли, что "наследование" есть обязательным элементом ООП?


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

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


Ну вот вы опять в своем духе. Можете укзать конкретные минусы, которые для Go гораздо критичнее, нежели для С++?

А в чьем духе мне ещё быть? Могу конечно, в каждой такой ветке их указываю:


  • в Go приоритет на локальность, простоту и понятность кода.
    По вашей же ссылке в списке cons есть два пункта, который очень противоречат этой идеологии. В С++ это, понятное дело, не проблема, там простота и понятность кода никогда не стояла приоритетом, объективно.
Это не означает, что в Go ООП нет.
Мне не охота вступать по этой теме в дискусси. Но судя по оффициальной FAQ:
Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy…
Тут можно долго дискутировать, но лично для меня «yes» никогда не будет равно «yes and no».

в Go приоритет на локальность, простоту и понятность кода.
Наверное именно поэтому есть panic/recover? Они то добавляют и простоты, и локальности, и понятности.

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

Незачем. Термин ООП придумал Алан Кей, и у него есть вполне четкие определения.


Наверное именно поэтому есть panic/recover? Они то добавляют и простоты, и локальности, и понятности.

Вы не понимаете, как panic и panic/recover в Go используются, и используете это незнание, чтобы поиронизировать? Ну успехов.

Термин ООП придумал Алан Кей, и у него есть вполне четкие определения.
Отлично. Сходу нашлось такое. Там вполне вариации на тему. Вы какого вариана определения придерживаетесь, там где есть «Every object is an instance of a class (which must be an object).» или «Classes are organized into a singly-rooted tree structure, called the inheritance hierarchy.». Возможно вы используете некое иное определение, тогда попрошу его привести, дабы не было разночнений.
Возможно вы используете некое иное определение,

Я рад, что вы начали открывать для себя новые грани ООП. Но рекомендую спорить по теме, когда вы её изучили, а не когда только начали узнавать и нагуглили первый результат. Возможно вам поможет вот эта моя статья понять глубже тему:
https://habrahabr.ru/post/243593/


Ну так поясните, вы же go-гуру, не я.

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

Я рад, что вы начали открывать для себя новые грани ООП. Но рекомендую спорить по теме, когда вы её изучили, а не когда только начали узнавать и нагуглили первый результат.
Так вы приведете «определение от Алана Кея» с которым вы согласны? Или цитата
OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.
и есть необходимое и достаточное определение ООП?

Заодно уточню. C вашей точки зрения применимо ли определение «extreme late-binding of all things» к Go или нет?
Подождите, но ведь по согласно приведенной вами цитате Кея ООП подразумевает «extreme late-binding of all things»… А к Go это не применимо?! Как же так получается то?
Так получается, когда выходишь за рамки бинарного определения вещей — «нужен/не нужен», «плохо/хорошо» и так далее. Надеюсь у вас с этим проблем нет?
Так получается, когда выходишь за рамки бинарного определения вещей — «нужен/не нужен», «плохо/хорошо» и так далее. Надеюсь у вас с этим проблем нет?
На личности переходите (уж который раз). Совсем-совсем кончились аргументы? (вопрос риторический, можете не отвечать).

Давайте по теме.
1) Вы сказал, что Go суть ООП язык.
2) Вы указали, что следуете определению от Кея.
2) Вы дали «определение» ООП по Кею (на самом деле нет).
4) Вы признали, что Go ему не соотвествует.

Тут явно кто-то лишний. Есть несколько вариантов выхода из противоречия (список не исчерпывающий):

1) Вы соглашаетесь что Go не ООП язык.
2) Вы утверждаете, что это не верное определение ООП (можно даже сказать что-то плохое про Кея) и приводите новое;
3) Молча ставите минус моему комментарию.
4) Еще сильнее пытаетесь перейти на личности и оскорбления.

Интересно, какой же вариант вы выберете.
ООП это не бинарный концепт — 1/0. Никакого перехода на личности в вопросе — нету ли у вас проблем с пониманием небинарных вещей нет. Нет ничего плохого в этом — многие мыслят бинарными категориями. Но мне подобные «дискуссии» не интересны. Желаю вам таки познакомиться с ООП поближе, а не устраивать детский сад с определениями, о которых вы узнали 15 минут назад.
нету ли у вас проблем с пониманием небинарных вещей
не устраивать детский сад
вы узнали 15 минут назад

Вот это все попытки перехода на личности если что.
Таки вы выбрали вариант 4, я немного разочарован :(

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

Но при этом попытались оскорбить и принизить оппонента… А потом удивляетесь, отчего на хабре такое «недружелюбное» сообщество. Быть может проблема вовсе не в сообществе?
Но ведь ООП в Go вообще нету

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


Сами по себе они никому не упали.

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

Можно наследовать структуры от интерфейсов и, отдавая их (интерфейсы) наружу получить инкапсуляцию и полиморфизм.
Не затруднитесь приложить код «наследования структуры от интерфейса»?

Прикладывать нечего — го автоматически делает структуру наследником интерфейса, если в ней определены все методы интерфейса при условии совпадения сигнатур.
Эта автоматика лично меня не радует.

Тогда приведите, пожалуйста, ссылочку на документацию Go, где сказано, что «структура наследуется от интерфейса». Имеено наследуется (inherits) а не реализует (implements).

В Go нет наследования. Это человек сам придумал.

В случае интерфейса что implements что inheritance — все едино.
Ибо у интерфейса все методы виртуальные и абстрактные, а полей нет. Хочешь отнаследоваться — просто реализуй их все, отсюда и понятие реализации интерфейса.
Соответственно говорить об их наследовании корректно (например, в плюсах наследование интерфейса реализовано именно как наследование от чисто абстрактного класса) и нет никакого смысла противопоставлять его реализации.

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


Не вводите людей в заблуждение, пожалуйста.

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

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

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

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

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

Никому ничего не навязываю. За что я люблю go после с/c++.
1. Нету точки с запятой ";"
2. Строгая типизация не даёт наделать ошибок.
3. При заполнение структуры можно вставить и вызов функции и какой то расчёт.
3. Нет классического ООП. Зато есть простые механизмы его заменяющие.
4. switch по string.
5. Функция может возвращать несколько значений
6. Горутины. Просто и удобно.
7. Есть только один for на все случаи жизни

Не относящееся к самому языку.
1. Библиотеки, lint, организация тестов всё из коробки и нормально работает.
2. Скорость компиляции. В разы быстрее. Когда проект большой, Ждать приходилось по 30 минут.

Что не нравится:
1. Паника в слайсах.

Не относящееся к языку.
1. Нет нормального отладчика. delve это издевательство над отладкой. брэк поинт во время работы не поставишь. Часто прыгает по коду как угодно. Как будет стоит оптимизация -О3. Но всё равно спасибо автору хорошо хоть такой есть. И минус гуглю в карму.

Эм… а в C++ не строгая типизация?

В C++ больше возможностей про приведению типов. и меньше контроля.
Например, Go не разрешает неявную конверсию int -> float, int32 -> int64…
Строгая, но слабая) (В просто Си, насчёт плюсов не знаю). Был интересный материал по этому поводу habrahabr.ru/post/161205
> 1. Нету точки с запятой ";"

Это сомнительный плюс. По крайней мере в той версии, в которой в Go:

>> When the input is broken into tokens, a semicolon is automatically inserted into the token stream immediately after a line's final token if that token is
>> an identifier
>> an integer, floating-point, imaginary, rune, or string literal
>> one of the keywords break, continue, fallthrough, or return
>> one of the operators and punctuation ++, --, ), ], or }

В результате я могу, например, написать
                x1 := (-b + dd) /
                        (2 * a)


но не могу

                x1 := (-b + dd)
                        / (2 * a)


Для сравнения, в Python правило выглядит так — он продолжает «подбирать» следующую строку, если есть незакрытая скобка любого вида, или если текущая строка заканчивается обратной косой:

                x1 = (-b + dd) \
                        / (2 * a)


или с незакрытой скобкой

                x1 = ((-b + dd)
                        / (2 * a))

Обычно таки языки, в которых есть автотерминация statementʼа завершением строки, дают средства продолжения. Python — показано выше. BASIC (Microsoft) — '_' в конце. Fortran с fixed format — не пробел и не '0' в 6-й позиции следующей строки, с free format — '&' в конце предыдущей строки. И так далее.

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

Точно то же с '{' для if, for — принудительно на той же строке, что условие. Это было понятно в Tcl, где тело в {...} — часть строки с особым режимом квотинга, но не в языке, где нет ограничения единственным типом данных «строка».

И, самое существенное, непонятно, чего они боялись тут. В Си может быть проблема типа такого, что незакрытая через ';' сущность будет дополнена следующим #include — такие проблемы иногда можно долго искать. А в Go что?

Ну и, очевидно, нежелание ';' — вопрос откровенной вкусовщины. Меня это не пугает (после Fortran, Python, shell и прочих), но и считать откровенным преимуществом — согласиться сложно. Не так уж сложно было их ставить :)

> Строгая типизация не даёт наделать ошибок.

Если сравнивать с каким-нибудь Javascript или PHP, то да. Если с более близкими языками, как C, то преимущества более сильной типизации откровенно не раскрыты.
Например, нет неявной конверсии int -> int32 и назад, даже если int 32-битный. Нет неявного сужения типа int32 -> int8. Но где удобные операторы конверсии с проверкой? Как мне проверить, что число влезло в int8 без потерь — конвертировать снова в int32 и сравнивать с исходным?
Нет неявной конверсии int -> float. OK, может, это даже полезно (на общепринятых размерах, int -> float это возможная потеря точности, в отличие от int -> double). Как мне провести конверсию с конкретным округлением и проверкой отсутствия реального округления? Тоже сравнивать результат обратной конверсии?

Зато хохмы типа типизированного nil и неожиданного nil != nil (если они ожидаются разных типов) — откровенная диверсия.

> При заполнение структуры можно вставить и вызов функции и какой то расчёт.

А где нельзя (и это настолько проблема)?

> Нет классического ООП. Зато есть простые механизмы его заменяющие.

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

> switch по string.

Это настолько важно?

> Функция может возвращать несколько значений

Да. Но уже давно не уникально.

> Горутины. Просто и удобно.

Нет, если мы выходим за пределы стандартных примеров :)
Как только начинаются проблемы типа «вот этому пулу действий дать гарантированные 40% процессора», реальный мир врывается и всё ломает. Сразу начинаются пляски с sync.* и тому подобными ужасами…
или каналы с особенностями

> Есть только один for на все случаи жизни

Ну это совсем ничтожное преимущество — ещё и сбивающее с толку.

Если сравнивать, то вот что я вижу положительного в Go:

1. select из каналов, вместе с самими каналами как средством взаимодействия. Это преимущество, в первую очередь, по сравнению с Erlang.
2. Принудительность {} вокруг тел if, for. Мы имеем это в своём стиле для C/C++/Java/etc., и это сильно помогает, но есть места, где настаивают на обратном для однооператорных тел (например, FreeBSD, Linux kernel) и регулярно ловят проблемы от этого. Преимущество не уникальное (см. хотя бы Swift). Редкий случай, когда форсирование стиля идёт на безусловную пользу.
3. Паскалеподобный порядок в декларациях (var x[:] int, а не int x) — упрощает понимание сложных случаев (не буду приводить хрестоматийные садистские примеры для C).
4. Жёстко определённые правила исполнения арифметики — дополнительный код с сохранением младших бит для +-*, выполнение сдвигов на всю ширину заданного сдвига. Никаких тебе «undefined behavior» стиля C, которые бьют по голове из-за угла даже очень опытным программистам. Иногда и более жёсткое (например, порядок вычисления аргументов функции) — сомнительно, но позволяет предсказывать побочные эффекты.
Тут хотелось бы иметь контекстное ослабление подобных правил (и бо́льшую свободу) для варианта «да, автор уверен», вплоть до того, как в C, и свободы оптимизации от этого — но, видно, не входит в представления о целевой группе.
5. Устранение некоторых наследственных кривостей C типа что '&' приоритет ниже, чем у арифметики (кто на это не нарывался — считает &, &&, *, / примерно одним уровнем — и удивляется последствиям). Но не всех кривостей :(
6. Определение переменных для контекста if, for в его входном условии (позднее перенесено в C++17).
7. defer (я не про panic/recover, а про defer вообще). Идея старая, но на уровне языка вроде только в Go (boost::scope надо ещё подключать).

Остальное или так же, как у аналогов, или кривее.
В Go же получается, по факту, дополнительное принуждение к конкретному стилю.

На фоне мейнстримных языков со свободным форматированием это скорее хорошо.


Зато хохмы типа типизированного nil и неожиданного nil != nil (если они ожидаются разных типов) — откровенная диверсия.

Хоар сделал ошибку на миллиард долларов. Авторы го смогли ее усугубить.


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

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


Как только начинаются проблемы типа «вот этому пулу действий дать гарантированные 40% процессора», реальный мир врывается и всё ломает.

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

Не хотел бы сильно развивать спор. И не на чём не настаиваю, но:
> 1. Нету точки с запятой ";"
Это сомнительный плюс. По крайней мере в той версии, в которой в Go:
> Есть только один for на все случаи жизни
Ну это совсем ничтожное преимущество — ещё и сбивающее с толку.

Ну и, очевидно, нежелание ';' — вопрос откровенной вкусовщины. Меня это не пугает (после Fortran, Python, shell и прочих), но и считать откровенным преимуществом — согласиться сложно. Не так уж сложно было их ставить :)

Я не считаю это вкусовщиной. Чем меньше понятий и больше ортогональности понятий, чем легче писать и меньше нужно держать в голове. Я знаю откуда точка с запятой в С, но с практической точки зрения для написания программы она для меня не имеет смысла. Тоже самое касается for. Мне нужно выучить только одно понятие. не три for, while, do которые по сути одно и тоже. И это позволяет мне больше концентрироваться на том, что я хочу получить а не на синтаксисе языка. IMHO пример с переносом мне кажется не существенным. Главное что вам не дадут ошибиться.
В Go же получается, по факту, дополнительное принуждение к конкретному стилю. Не то чтобы это выходило за общие рамки — там таких принуждений вагон и маленькая тележка, и они громко выдаются за преимущества языка — но всё равно выглядит насилием.

Я не хочу сказать что мне нравится насилие. Но в данном контексте я считаю это правильным. Зачем делать «свободу выбора», чтобы потом писать инструкции по стилю написания программ? Не проще ли принять стиль Go и потом вообще забыть про стиль? Линт меня например совсем расслабил, в Go я вообще не форматирую код. Всё это делается автоматически, что экономит мне массу времени. И мне всё равно какой тип переноса, главное что мне не дают ошибиться.

> При заполнение структуры можно вставить и вызов функции и какой то расчёт.
А где нельзя (и это настолько проблема)?
> switch по string.
Это настолько важно?
> Функция может возвращать несколько значений
Да. Но уже давно не уникально.

Я же писал Go по сравнению с с/c++. Это сильно упрощает конструкции и код становится меньше и более понятным.
> Горутины. Просто и удобно.
Нет, если мы выходим за пределы стандартных примеров :)

Возможно я не дорос до таких высот использования горутин, чтобы уметь ей дать 40% процессора. Небыло необходимости. Умея обычно горутина или спит или работает. По этому здесь сказать ничего не могу.
Перешел с С на Go. Жалею, что не сделал это раньше. Не хочу сказать, что C не нужен, просто большую часть задач можно написать на Go потратив значительно меньше времени.
Попробуйте D будете удивлены тем на сколько меньше кода придется писать..))
Да, кстати, видел сравнение кода этих языков, но мне пока бы с Go разобраться)

В каждый тред где человек пишет, что перешёл на Go, ему советуют попробовать D. Если человек пишет что перешёл на Rust, то у него спрашивают: «Почему не на D»?


Похоже у D слишком плохо с PR-ом, раз на него не переходят :)

Статью не читай, сразу комментируй )

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

Нами, ага. Так себе «наблюдение», на самом деле )
Only those users with full accounts are able to leave comments. Log in, please.

Articles