Comments 138
Дело привычки.
А кроме того, вопрос пишущим на Питоне: когда строка такой длинны, что не помещается на экран, можно ли сделать перенос начинающийся с отступа для удобства чтения? И как такое воспринимает Питон?
Можно использовать символ \
для переноса строки:
foo = 2362366232 + 1423204837 - \
1744037725 * 47494036263
Но лучше использовать скобки. В Python можно обвернуть в скобки любое выражение, и тогда его можно переносить на новую строку.
foo = (2362366232 + 1423204837 -
1744037725 * 47494036263)
Отступ перенесённой строки в этом случае роли не играет, но обычно придерживаются правил с учётом Style Guide
но не все любят когда их заставляют из под палки
Гораздо больше людей не любят читать плохо отформатированный код. Для этого принимают Style Guide и придерживаются его. Поэтому разработчику, если он работает над проектом в команде, всё равно придётся придерживаться какого-то стиля форматирования, а не писать как попало.
Незакрытые скобки должен подсвечивать ваш редактор кода.
Если я вынужден писать одно и то же через каждую строку-две, это — бесполезно.
Если вы пишете одно и то же через строку две больше чем два или три раза — то это уже повод для редизайна вашей программы. Кроме того, Go вас не ограничивает — если вы думаете, что единственное, что можно делать с ошибками в Go — это проверять на nil, то это грубая ошибка. Вы можете делать с ними все что хотите, хоть по каналам передавать — что будет лучше для логики вашей программы.
Для меня «бесполезно» — это когда эксепшены повсюду, и когда на практике программа встречает ошибочные ситуации, ценность этой «экономии» строк кода оказывается нулевой — чаще всего эксепшены используются не так, как написано в учебниках, а как «не хочу думать про ошибки, выкину и там наверху кто-то разберется с ней». Увы.
Самое интересное, что для большинства практических применений лучшая обработка ошибок будет в подходе "Let It Crash". Проверять коды возврата, ловить исключения, — это всё равно путь в никуда при обработке настоящих исключительных ситуаций (таких, которые не должны произойти), в нетривиальной программе вы по-любому пропустите обработку какого-нибудь кода возврата, при этом мало того, что код будет захламлён, так ещё и варианта восстановления именно на этот случай не будет.
Вот нужно открыть файл, прочитать оттуда, записать туда, закрыть файл. Если что-то не смогли, то дальнейшая работа должна остановиться. Отсюда получаем, что нужно 4 проверки. С исключениями это все превращается в один перехватчик.
Более того, в большей части приложений перехватчики нужны в основном на границах модулей, что позволяет избежать написания огромной кучи кода. А, как известно, лучший вид кода — это отсутствующий код. Его не нужно поддерживать, там нет багов, он даже не требует времени на написание.
Более того, современные способы работы с исключениями не создают дополнительного оверхеда, в отличии от проверки ошибок.
Проверять ошибки вместо обработки исключений — это простейший способ бесполезно потратить кучу времени.
Вы не правы. Исключения создают стимул не заботиться о правильной обработке ошибок, а надеяться на язык. Выкинул, а там кто-то сверху уже разрулит. Недавний анализ всех публичных Java репозиториев на гитхабе расставил тут точки над i. Поэтому вы выигрываете три секунды в некоторых случаях, но проигрываете гораздо больше, когда в продакшене происходит ошибка, ценность которой ноль, потому что у вас общий перехватчик наверху, который выводит General exception.
Вот нужно открыть файл, прочитать оттуда, записать туда, закрыть файл. Если что-то не смогли, то дальнейшая работа должна остановиться. Отсюда получаем, что нужно 4 проверки. С исключениями это все превращается в один перехватчик.
А если вам нужно при ошибке открытия файла, попытаться открыть другой файл? А если при ошибке чтения вам нужно использовать дефолтное значение? А если при записи, вы должны залоггировать ошибку, а при закрытии — проигнорировать?
Явная проверка ошибок имеет дикое преимущество перед исключениями в том, что вы, читая код, сразу же видите, что будет происходит — вы сразу же понимаете flow программы. Это избавляет вас от надобности прыгать по уровням и модулям, в поискам, где же кто и как обработает тот или иной вид ошибки.
В Go ошибки — это такие же значения, как и любые другие. Вы можете делать с ними что хотите, что будет лучше для вашего случая. Хоть передавайте каналы каналов ошибок — но чаще всего, правильная обработка ошибки — это, как ни банально, тупая проверка — «случилась ошибка — сделай-то то-то».
А, как известно, лучший вид кода — это отсутствующий код
Эта фраза имеет в оригинале другой контекст. «Отсутствующий код для проверки ошибок» — это точно не лучший вид кода, вот гарантирую.
Более того, современные способы работы с исключениями не создают дополнительного оверхеда, в отличии от проверки ошибок.
Проверка ошибки — это одна инструкция на ассемблере. Какие «современные способы работы с исключениями» будут быстрее?
Вы понимаете, что такое исключительная ситуация?
А если вам нужно при ошибке открытия файла, попытаться открыть другой файл?
То это не исключение, т.к. у нас есть другой файл.
А если при ошибке чтения вам нужно использовать дефолтное значение?
То это не исключение, т.к. у нас есть дефолтное значение.
А если при записи, вы должны залоггировать ошибку, а при закрытии — проигнорировать?
То это прописанная бизнес-логика.
Всё, что Вы приводите в качестве примера, строго говоря, не имеет отношения к исключительным ситуациям. Т.к. исключение — это неожидаемое поведение, обработка которого не описана в требованиях.
«Отсутствующий код для проверки ошибок» — это точно не лучший вид кода, вот гарантирую.
См. мой комментарий чуть выше. Разработчики отказоустойчивых систем с Вами категорически не согласны :-)
Т.е. если это «прописанная бизнес-логика» — надо пользоваться return error.
А если «исключительная ситуация» — надо пользоватсья panic/recover.
Я знаю, просто divan0 смешивает запасной вариант и исключение на уровне понимания.
В зависимости от контекста одна и та же вызов функции может приводить и к тому и к другому, но концептуально это разные вещи. Поэтому во многих языках библиотечные функции имеют 2 формы: одна возвращает статус и результат, а вторая — только результат, а в случае ошибки кидает исключение.
В Go для второго случая придётся в куче мест писать if err != nil { panic(err) }
Насчет Go, ну напишешь так и всем будет ясно, что тут происходит. Это называется более ясный и читаемый код. Поверьте, видеть код с явной обработкой ошибок существенно упрощает его понимание. Я тоже не понимал ошибок Go до тех пор, пока не написал пару проектов нормальных на нем. Обработка ошибок не доставляет никаких проблем и только упрощает понимание того, как себя код может вести. Сколько раз в языках с исключениями я задавался вопросом — а какое тут может исключение и что будет, если оно вылетит, кто его обработает, где. Где-то это код упрощает, где-то нет и делает его сложнее для понимания.
Это в каких языках?
Ну, например, в Elixir. Там по соглашению, имена методов, которые могут вызвать исключение заканчиваются на !
. Допустим, File.open и File.open!. Весьма удобно для разделения штатных и исключительных ситуаций на уровне кода.
P.S. Обратите внимание, что я не говорю, что использовать повсюду исключения — это хорошо, наоборот, это так же плохо как повсюду проверять код возврата :-)
А главная проблема в том, что нет изоляции на уровне пакетов. Если в чужом коде возникла ошибка, она имеет все шансы добраться до вашего основного приложения и даже уронить его.
panic
. И то, при большом желании их можно поймать с помощью recover
.Давайте лучше не вводить искусственных ограничений… Если говорить о том, что лучше, то равняться надо на лучшие подходы, а не только на самые популярные в данный момент (C#, Java) и самые старинные (C, Go).
Раз уж С++ был упомянут, то там местами подход "дублирования" функций тоже используется. И в стандартной библиотеке: dynamic_cast в ссылку/указатель, nothrow new и т.д. Примеров не так много, но это скорее потому исключения не особо активно используются. Справедливости ради, можно найти места, где используются только они (например, для регекспов). И в некоторых популярных билиотеках типа буста: функции имеют перегрузку с дополнительным параметром типа system::error_code именно для возврата ошибки без исключения.
Ну и ещё можно вспомнить, что вещи типа std::optional почти дошли до стандартизации и полезны, в том числе, при нежелании кидать исключения.
Разработчики отказоустойчивых систем с Вами категорически не согласны :-)
Я сам разработчик отказоустойчивых систем, и это мы с вами не согласны. :)
Когда люди обсуждают обработку ошибок, подразумевается не только «нехватка памяти на сервере», а множество других вариантов того, что подразумевается под ошибкой. Так что давайте или на одном языке говорить или мы так не построим разговор.
Как нескромно, о себе во множественном числе ))) Впрочем, давайте не будем опытом мериться.
Лучше приведите хоть 1 пример исключительной ситуации, когда проверка статуса возврата хоть чем-то лучше Let It Crash. Пока Вы приводите примеры штатного поведения… Применять к ним термины "ошибка" и "исключение", на мой взгляд, в принципе некорректно. В случае же исключительной ситуации единственное возможное поведение — это как можно быстрее привести в систему в рабочее состояние после сбоя.
Речь здесь о том, что в языках с исключениями нет разделения на исключительные ситуации, когда нужно только падать, и просто ошибками, которые можно обработать и без всяких последствий продолжить работу, либо чуть подкорректировать ее. И если говорить о Go, то именно он это разделение делать позволяет очень просто — panic это исключительная ситуация, а все остальное ошибки, которые можно и нужно обработать.
И поверьте, это прекрасно работает для этого языка. Моя история комментариев здесь покажет, что я был один из тех, кто ругал язык за его подход к ошибкам и отказ от исключений. После того, как я реально сел и написал на нем что-то, мнение это поменялось на прямо противоположное.
Timeout в Java и C# являются исключительной ситуацией, но в большинстве случаев это не Let It Crash, а стандартное поведение того же сетевого кода, которое можно или игнорировать, или использовать для попытки переподключения.
Так Let It Crash как раз и организует Вам переподключение. Это дефолтное поведение в рамках этой методологии: убить корутину и перезапустить её с чистого листа, чтобы ещё раз попробовать.
Речь здесь о том, что в языках с исключениями нет разделения на исключительные ситуации, когда нужно только падать, и просто ошибками, которые можно обработать и без всяких последствий продолжить работу, либо чуть подкорректировать ее.
Ну это скорее от стандартной библиотеки ЯП зависит, а не от факта поддержки исключений. Ну и даже если stdlib подкачала, есть же варианты… Например, перед открытием файла можно проверить, что он существует. Да это не гарантирует, что он откроется, но всё же если он существовал на момент проверки, но не открылся — это уже будет ближе к исключительной ситуации.
Нет нет и нет. Проверять существование файла перед открытием верный путь, чтобы отстрелить себе ногу. Нужно именно что открывать и смотреть, что вернется.
Пока Вы приводите примеры штатного поведения…
Вот здесь мы с вами расходимся. К примеру, при неудаче открыть файл, в зависимости от задачи, это может быть (в вашей терминологии), как «штатное поведение», так и «исключительная ситуация» — это определяется вашей бизнес-логикой.
приведите хоть 1 пример исключительной ситуации, когда проверка статуса возврата хоть чем-то лучше Let It Crash
Приведите, чтобы не терять время, для начала пример «исключительной ситуации» (в вашем понимании) в приведенном же вами примере — открытие и чтение из файла. Просто чтобы мы синхронизировались в терминологии.
чтобы не терять время, для начала пример «исключительной ситуации» (в вашем понимании) в приведенном же вами примере — открытие и чтение из файла.
Например, пользователи загружают на сервер файлы для какого-то преобразования… для этого преобразования мы используем внешнюю программу. После её успешного завершения, мы читаем результат из файла, путь к которому передан программе. Если при этом файл открыть не удаётся, то это исключительная ситуация.
как «штатное поведение», так и «исключительная ситуация» — это определяется вашей бизнес-логикой.
Исключительные ситуации на то и исключительные, что они не определяются бизнес-логикой. Бизнес-логика даже не догадывается о том, что они вообще возможны.
… Если при этом файл открыть не удаётся, то это исключительная ситуация.
Бизнес-логика даже не догадывается о том, что они вообще возможны.
Вот тут я вижу в ваших словах непримиримое противоречие. Ошибка при открытии файла само по себе может вполне быть «штатным поведением» — например, мы пытаемся открыть "~/.ssh/id_rsa.pub", а у пользователя ECDSA-ключ, поэтому мы должны попробовать файл с другим именем.
И вот давайте посмотрим — функция для открытия файла — которая занимается как раз всей низкоуровневой работой, она может встретить ошибочную ситуацию — файла нет, или доступ запрещён или ещё что — и вот что она должна сделать? Вернуть ошибку вашей программе, как бы говоря — «я свою работу выполнила, а как уже эту ошибку трактовать — это тебе решать», или выкинуть «исключение», сказав — «это исключительная ситуация, падай и туши все»?
Ведь вашем и моём примере, «ошибка при открытия файла» — это разные вещи и диктуется это исключительно бизнес-логикой.
Вот тут я вижу в ваших словах непримиримое противоречие.
Не мудрено увидеть противоречия если выдрать 2 предложения из разных контекстов ))) Я нигде не писал, что ошибка при открытии файла не может быть штатным поведением, даже наоборот я писал чуть выше, что она может быть и тем и тем. Вы попросили пример когда она является исключительной ситуацией. А сами привели пример, когда она является штатным поведением. Ну как бы и что дальше?
функция для открытия файла — которая занимается как раз всей низкоуровневой работой, она может встретить ошибочную ситуацию — файла нет, или доступ запрещён или ещё что — и вот что она должна сделать?
Очевидно же, она должна позволять работать в обоих режимах. Если я ожидаю ошибку, то вызову вариант возвращающий статус, если нет — то вариант, кидающий исключение. Я начинаю понимать, что Вы смотрите на этот вопрос с позиции, что надо выбрать что-то одно, но нет! Необходимости выбирать что-то одно для всех случаев тут нет.
В Go вы пишете эту логику как есть — «если случилась ошибка, и надо упасть — падай» или «если случилась ошибка, и надо попробовать другой файл — пробуй». Программист, читающий этот код сразу понимает, что происходит и идёт дальше. Компилятор, читающий этот код, тоже сразу все понимает: есть ошибка — исполняй это, нет ошибки — исполняй это. В сущности, так оно и есть же.
С исключениями, вы, во первых, с трудом вообще знаете — может там быть ошибка или нет. Но, допустим, знаете. Кто эту ошибку словит — код на уровень выше? На два? На три? Если нужно реализовать бизнес-логику — вам нужно всё равно писать catch с описанием типа исключения (которые всё равно не используют толком, гг), при этом еще try и скобочки — всё это становится гораздо более громоздко. Не говоря уже о том, что flow кода становится разбросанным по разным частям кода, что затрудняет понимания. А всё это вместе создает стимул — ведь это же главное преимущество исключений! — написать один общий catch блок и там просто вывести ошибку, а потом уже решать, хорошо это или плохо.
Про перформанс исключений я молчу вообще — это же тоже не бесплатно.
Есть один кейс, когда проброс исключений действительно дает выгодну в виде экономии строк — когда у вас несколько повторяющихся вызовов, и обработка ошибок однотипна (пока что, при рефакторинге может же измениться, и тогда выгода уже не такая явная). Но тут всё просто — если у вас больше трех однотипных вызовов, то, скорее всего, вы пишете простыню и это повод рефакторить вашу функцию или пересмотреть дизайн.
Впрочем, если это не «однотипные», а одинаковые вызовы (например, запись побайтово команд какого-то протокола — тоже ж бывает), то никто вам не мешает написать не if err, а создать враппер вокруг функции write(), который будет проверять ошибку и выбрасывать panic — вот тут этот пример рассмотрен.
Вопрос — ради этого кейса и спорной экономии трех-четырех строк, готовы ли вы пожертвовать читабельностью, производительностью и понятностью кода во всех остальных случаях? Я — нет.
Я не пойму, у Вас на эту тему какая-то домашняя заготовка что-ли?
Я Вам про Let It Crash, а Вы мне про "кто эту ошибку словит", "try и скобочки"… Не надо исключения перехватывать, они на то и исключения, чтобы уронить подпроцесс и дать ему перезапуститься с чистого листа.
Непонятно в каком месте тут жертвы в читабельности, производительности и понятности? Кода обработки исключений нет вообще, читаемость и понятность у happy path самая шикарная из всех возможных. Если у Вас исключения происходят настолько часто, что затрагивают производительность, то либо Вы их используете для штатных ситуаций (control flow), либо у Вас общесистемные проблемы, возможно SSD на сервере пора менять или что-то в этом роде.
Я Вам про Let It Crash, а Вы мне про «кто эту ошибку словит»
Именно, потому что я пытаюсь вам объяснить про подход к обработке ошибок в Go, а вы отвечаете — «это не ошибка, это штатное поведение».
Кода обработки исключений нет вообще, читаемость и понятность у happy path самая шикарная из всех возможных
Супер, но помимо «исключений» есть ещё тысяча вариантов того, что вы называете «штатным поведением», и вот с ними тоже нужно иметь дело.
Кстати, если так посмотреть, то ваше мнение как раз наилучшим образом демонстрирует философию ошибок Go — «это не ошибки, это штатное поведение, логика программы». Это вот это самое «errors are values». Go именно это говорит — ошибки это не специальные магические вещи, это такая же логика и такие же значения.
В Go не хватает нативной поддержки варианта, когда это нештатное поведение. Достаточно было бы сделать аля сопоставление с образцом
file, nil = os.Open("file.txt")
Но так как этого нет, то происходит навязывание рассматривать абсолютно все ошибки, как штатное поведение. Это порождает совершенно лишний boilerplate-код. И я не понимаю, о чём тут спорить… Попробуйте хоть один ЯП, который не принуждает Вас выбирать что-то одно (код возврата vs исключение) в 100% случаев и сами всё поймёте.
И самое главное, язык построен так, что все его возможности отлично подыгрывают этому.
defer
позволяет избежать стандартной проблемы С, когда нужен goto
и без него никак. Это позволяет избежать ситуации, когда во многих языках одно значение вернется из функции, другое через указатель в аргументах. Это не только усложняет вызывающий код, но и код самой функции. Этим грешат даже современные языки. Возврат нескольких значений из функции отлично подыгрывает функциям, которые возвращают результат и ошибку.И в итоге, этот «boilerplate» код ничуть не усложняет код, а делает его только яснее, потому что сам язык проповедует подход, что ошибки это не boilerplate, а логика программы. Если бы не было всех этих фич, то конечно же мы получили бы C/С++, где обработка ошибок это пытка.
Вот тут между нами разница. Я писал на Go, в том числе для production. Вы же тот подход, о котором я пишу, на практике не пробовали, а сравнить пытаетесь на основании субъективных предубеждений. Для меня нет сомнений, что если Вы попробуете Let It Crash, то тоже осознаете, что подход Go примерно так же коряв, как подход Java. Те же грабли, только в профиль.
Это порождает совершенно лишний boilerplate-код. И я не понимаю, о чём тут спорить…
Ну вы не хотите понять. Это не boilerplate-код, это просто «код». Который не менее важен, чем happy path. Подход Go уравнивает их.
Для меня нет сомнений, что если Вы попробуете Let It Crash
Этот подход хорош для очень узкого круга задач и очень маленького класса ошибок (которые вы называете исключениями). Зачем на этом зацикливаться? Вы будете ронять GUI приложени? Или веб-сервер, которые получил неверный вход? Или (ваше же пример выше) — не найден файл, вы уронили программу — и что, файл появится?
Обработка ошибок — это гораздо более широкая тема и подход Go очень удачно добивается того, что в результате в программах на Go ошибкам уделяется должное внимание. Не это ли важно в конце концов.
Этот подход хорош для очень узкого круга задач
Этот подход идеален для любых видов веб-разработки. А что-то другое я на практике редко программирую, поэтому за остальные области судить не буду.
вы уронили программу
Let It Crash в принципе не предполагает ронять программу, скорее наоборот. Практически ничто не способно уронить программу, написанную в таком стиле.
Прочитайте, пожалуйста, маленькую статью на эту тему LetItCrash. И когда будете в ней встречать слово process мысленно переводите его как корутина, а не как процесс уровня OS, так будет понятнее суть. А то получается, что Вы какой-то свой смысл вложили в термин и исходя из него делаете неверные выводы.
Вы будете ронять GUI приложени?
Нет, вычисления будут проводиться в отдельном акторе (корутине, go-рутине, легковесном процессе), если что-то пойдёт не так, то перезапустится конкретный актор, а не всё приложение целиком.
Или веб-сервер, которые получил неверный вход?
Если входные данные невозможно обработать, то упадёт не сервер, а один из воркеров, которые обрабатывают входные данные. После чего он сразу будет перезапущен супервизором и будет готов принимать новые входные данные с чистого листа. Это кардинально отличается от ручного recover, когда вы рискуете не идеально восстановить состояние go-рутины после сбоя.
Это не boilerplate-код, это просто «код».
Ох, мы опять по второму кругу… Просто код — это всё тот же happy path (как в вашем примере открой либо первый файл, либо второй)
А boilerplate-код это:
res, err := some.Function();
panic(err)
И ещё recover, сиротливо воткнутый куда-то.
Ладно, я вас понял — про ошибки вам не интересно, вам "исключения" неудобно в Go делать, потому что нужно явно писать для них код. Я вам пытаюсь, тем более, донести то, что говорили другие комментаторы в этой ветке — а вы то к термину "исключение" прицепились и всё запутали.
Если воспользоваться метафорой, то я Вам про BMW X6 рассказываю, а Вы мне упорно доказываете, что Дэу Нексия лучше Лады Веста.
Проблема Go не в том, что сложно написать panic(err)
, а в том что в нём нет поддержки recovery-политик от слова "совсем". Ну и да, проверять err в каждой штатной ситуации это тоже на порядки неудобнее сопоставления с образцом.
Например, написать вот так (основной happy path отдельно, обработка штатных ошибок отдельно, исключений тут нет) в Go невозможно by design.
with {:ok, res1} <- first_function(),
{:ok, res2} <- second_function(res1),
:ok <- save_result(res2) do
:ok
else
{:error, err} ->
case err.type do
Postgrex.Error ->
# handle err
_ -> ...
end
end
А зачастую бывает удобно именно так. При этом вариант написать аля Go никуда не исчезает для особо разветвлённых happy paths.
Впрочем, насколько я понял, читать про что-то отсутствующее в Go, а тем более изучать, Вы не желаете… Посему опять пытаетесь съехать на хорошо знакомую Вам тему сравнения Дэу с Ладой )))
а вы то к термину "исключение" прицепились и всё запутали.
Ахах, т.е. я виноват, что у вас термин "исключение" при обсуждении Go ассоциируется с control-flow и Java?
CopyBytes
из статьи) и макрошаблоны (пример с Max
с сайта). Очень интересно, как нужно будет обращаться к этим функциям из Go.Точнее, смотрите, как на образец «что будет, если их даже не планировать, но потом в какой-то момент осознать, что так жить дальше нельзя». Это сейчас настолько повально распространенная вещь для статически типизированных языков, что вполне должна входить в общую образовательную базу.
По теме: Proposal, FAQ, RFC
Показательно, что RFC по нему открыли только в апреле этого года.
На Java как образец дженериков не смотрите.
Так я ж и говорю, что там отвратительные =) Хороши в C#, там тоже добавили не сразу, но поступили мудро, что забили на первые версии и теперь имеют нормальные дженерики.
Просто не получится ли с Гоу так же как с Джавой — вставят какую-то фигню через пару лет типа type erasure и потом все плеваться будут, а они только плечами двигать, мол обратная совместимость, раньше думать надо было?
Собственно, я к этой теме потерял интересно давно и совершенно не жалею, что этого нет в языке. Больше беспокоят всякие мелочи вроде невозможности использовать := для одновременного объявления переменной и присвоения значения полю структуры. Приходится заводить заранее переменную и использовать обычный =. Мелочь, а встречается намного чаще, чем необходимость в дженериках.
Я не сильно вдавался, но на поверхности выходит, что Go довольно уникальный язык и на него плохо ложатся обычные дженерик как у всех
Я вот несколько раз то обуждение прочитал. Может потому что английский у меня не идеален, но я так толком и не понял, почему им не нравятся Дженерики из c#. Про С++ есть прямо: «The problem with C++ generics (without concepts) is that the generic code is the specification of the generic type. That's what creates the incomprehensible error messages. Generic code should not be the specification of a generic type». К сожалению я не совсем понял, т.к. не знаю С++.
Про Джаву тоже: «unlike in Java, so you can find out types at run time. You can also instantiate on primitive types, and you don't get implicit boxing in the places you really don't want it: arrays of T where T is a primitive». Но вот в C# нету такой проблемы.
Мне особенно «нравятся» вот такие аргументы: «One key aspect of Go is that when you write a Go program, most of what you write is code. This is different from C++ and Java, where much more of what you write is types. Genus seems to be mostly about types: you write constraints and models, rather than code». Больше подходит для политического лозунга, а не технического обсуждения.
«Мелочь, а встречается намного чаще, чем необходимость в дженериках» мне, все-таки, кажется, что необходимость в них может варьироваться зависимо от домена.
The problem with C++ generics (without concepts) is that the generic code is the specification of the generic type. That's what creates the incomprehensible error messages. Generic code should not be the specification of a generic type
Тут речь идет о том, что если мы используем какой-то шаблон, ошибка выявится глубоко внутри библиотеки, там где она попробует воспользоваться заданным при специализации типом. Они предлагают определять сразу, подходит ли какой-то тип для специализации шаблона, или нет.
Дженерики были бы, если бы кто-то предоставил подобающее решение, которое устроит команду разработчиков.
Хм, интересная позиция… Разве команда разработчиков не в состоянии разработать решение, которое её же и устроит?
Ну, я не очень в теме как выстроен процесс разработки Go. Но со стороны это выглядит примерно так: "Мы тут накосячили с архитектурой языка так, что даже сами не можем придумать решение, но у нас Open Source, так что давайте теперь как-нибудь силами сообщества вопрос порешайте, а мы рассмотрим ваши предложения, может быть.."
А предложение довольно простое — возможность объявить альтернативное имя для типа/функции/т.д. Что-то вроде
const C => L1.C // for regularity only, same effect as const C = L1.C
type T => L1.T // T is an alias for type L1.T
var V => L1.V // V is an alias for variable L1.V
func F => L1.F // F is an alias for function L1.F
Основной аргумент в пользу — упрощения рефакторинга для больших кодовых баз.
Про все остальные языки я конечно погорячился, но это есть в Rust и Swift как минимум.
В Rust, Swift и вообще эта конструкция связана не с абстрактными типам/константами/переменными/функциями а с импортом
Да ладно?
type MyMegaInt = i32;
typealias AudioSample = UInt16
Про Go ничего и не говорил. Речь о том, что "синонимы" типам в расте и свифте можно давать независимо от импорта. Или я всё-таки что-то не так понял?
Но всё-таки, если Go так умеет, то можно разжевать в чём же суть предложения, которое обсуждалось выше?
Теперь понятно, спасибо.
Тем не менее, в расте (насчёт свифта уже не уверен) оно всё равно работает и для импорта и без него.
В Have реализована работа с шаблонами. Разработчики Google не стали добавлять ее в Go из соображений простоты
Их не стали добавлять из-за отсутствия реализации, которая отвечает требованиям команды разработчиков. Они довольно серьезные и не только из-за капризов разработчиков, но и других конструкций языка, с которыми шаблоны должны быть прекрасно совместимы. Issue на гитхабе открыта для предложений.
реализация обработки ошибок в Go требует бесполезных затрат времени для написания однотипного кода.
Что?
Начинание правильное, но, по-моему, не те вещи он начал исправлять. Больше похоже на очередной проект, в который человек просто добавил все, что ему захотелось из других языков. Лучше было начать с github и открыть все быги с тегом Go2 — там предостаточно отличных идей и предложений, которые довольно не скоро появятся официально, т.к. судьба Go2 пока еще мутная.
struct A:
x int
func String() string:
return fmt.Sprintf("x:%d", a.x)
Я одно не понял, а откуда тут имя 'a'? Похоже на ошибку копипаста.
В целом, мне нравится новый синтаксис — стало нагляднее.
Возможно и не опечатка, в го по идеи должно было быть написано так:
func (a A) String() string
Тут возможно есть какое то соглашению по тому как генерируются имя переменной, чтобы обращаться к полям структуры.
What about pointer receivers? They are there, too. You declare them by putting a * before function name. The method receiver is always referred to as self.
struct A:
counter int
func *Inc():
self.counter++
Кстати, странно, что про этот куда более используемый кейс — ни слова.
Пример:
if (a == 1) a++; b++;
Это C, если что. Какое количество ошибок в куче программ повылазило только из-за того, что блок может состоять только из одного оператора…
Вы не поверите — в Питоне нет невидимых ошибок из-за отступов. Зато есть одинаковое понимание отступов человеком и компилятором.
Кстати, самый зрячий на ошибки компилятор у хаскеля. При той же отступной грамматике.
Необязательный?
Начните коммитить код без отступов — и команда вас проклянет.
Начните коммитить код с отступами, не согласованными со скобками — и коллеги вас убьют, а суд их оправдает.
А компилятору все равно.
Принимаются стандарты оформления, которые делают несоответствия между скобками и отступами максимально заметными.
Отступные грамматики делают чтение отступов у транслятора таким же, как у человека. Теперь плохое оформление отступами отслеживается раньше, чем код-ревью и вообще автоматически.
Заодно экономится куча строк, ранее занимаемых скобками.
Один из признаков того, что язык программирования имеет успех, – появление новых языков на его основе. Известным примером является JavaScript. На его базе возникли такие языки, как TypeScript, GorillaScript, Kaffeine, Sweet.js и так далее.
Несколько наивное утверждение. Главная причина появления новых языков «на базе JavaScript» — то, что он встроен в браузеры. Это как говорить о успехе машинных кодов x86 как языка программирования на основе того, что C, C++, Pascal, Haskell, Go и др. компилируются в них. Вторая причина — то, что сам он достаточно неудобен, чтобы многим хотелось как-то его улучшить и/или чем-то его заменить.
А не будь язык успешен, то никто не стал бы даже думать о таких вещах.
Один из признаков того, что язык программирования имеет успех, – появление новых языков на его основе. Известным примером является JavaScript.
Имхо, появление транспилеров — это признак того, что на исходном языке писать жутко неудобно/неприятно/etc., но какие-то другие факторы принуждают к этому. И JavaScript — яркий тому пример.
Своеобразный лайфхак — писать на приятном языке, а генерацию кода на неприятном языке поручить транспилеру. Разве есть другие причины писать транспилер?
Первая реакция: Фу!
(Это чисто мое мнение, оно может не / не должно совпадать с мнением большинства)
С питоном нужно все делать вручную и каждый раз, потому что там выравнивание — часть синтаксиса языка.
многие проблемы Go в нем не решены. К примеру, реализация обработки ошибок в Go требует бесполезных затрат времени для написания однотипного кода.
Это смешно, правда.
Эксепшены уже доказали свою несостоятельность, и в Google неспроста от них отказались.
Go заставит вас уважать ошибки и обрабатывать их всегда. Вы можете, как и большинство начинающих программистов, их игнорировать, забивать на ошибки, считать их чем-то несущественным, надеяться на магию языка, который разрулит ошибочные ситуации за вас, но ни один из этих подходов ни к чему хорошему не приводил, и Go в данном случае — ваш более опытный друг, который говорит — «мне всё равно, что ты привык писать плохой код, со мной тебе придётся уважать ошибки и уделять им должное внимание».
Эксепшены уже доказали свою несостоятельность,
Угу, общее, широкое утверждение и милая ссылка на специализированную статью по Java.
Вы, наверное, понимаете, что в мире C++ с исключениями всё очень и очень неоднозначно. И что Гугл отказался от их использования в C++ по совсем другим причинам. Которые совсем никак не связаны с тем, что в Java проверяемые исключения.
У исключений есть свои плюсы, но их минусы, которые не сразу очевидны в краткосрочной перспективе, таки перевешивают. Этим баталиям много лет, и я рад, что в Go нет исключений — уже много раз видел, как Go меняет отношение программистов к ошибкам, и как выигрывает от этого код в долгосрочной перспективе. Найти Go код, в котором ошибки игнорируются или неправильно обрабатываются — сложно.
Хобби-проект польского разработчика — усовершенствованная и дополненная версия языка Go