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

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

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

По счастливой случайности у меня больше никогда не было возможности поработать с C. Курсы бакалавриата, которые я посещал, так или иначе заставляли использовать java, много лет работал C++, затем ruby, C#, Java, Scala и многие другие языки. Языки, которые я изучил, со временем становились все более сложными, каждый язык сам по себе со временем приобретал все больше возможностей. Я думал, что это естественно, промышленность изучает полезные концепции, и эти концепции переходят в языки. Время компиляции увеличилось, DSL появились для всего, просто использование контроля версий стало болезненным с монорепозиториями. Исходный код для используемых библиотек начал гроковаться все дольше и дольше. Я понимаю, у всего есть веские причины, и я подумал: «Ммм», жизнь не так уж и плоха.

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

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

В течение многих лет общепринятым мнением в отрасли было то, что потоки обходятся дорого, поэтому единственный способ добиться эффективности — это сделать вещи асинхронными. Создание асинхронных вещей добавляет много сложности, даже если вы используете Futures на таком языке, как scala. Обычно можно увидеть такие вещи, как Future[Map[Key, Future[Option[Value]]]. Это влияет на читабельность. Много размышлений и времени уходит на разработку вещей, которые работают асинхронно, например. ограничители скорости должны быть переработаны и т. д. Стеки вызовов становятся менее полезными, структуры данных, которые вы используете, должны быть безопасными для параллелизма большую часть времени, в целом это в основном замедляет вас. Но, честно говоря, Futures — лучшая асинхронная абстракция, с которой я когда-либо работал. Но что, если вам не нужна асинхронность большую часть времени? Были попытки использовать зеленые нити, но почему-то они не пользовались популярностью. Голанг исправляет это. Горутины, которые являются эквивалентами потоков, действительно дешевы. Вы можете создать миллионы из них и не стесняйтесь блокировать их сколько угодно. Переключение контекста между горутинами тоже супердешево. Я знаю, что это не совсем так, но позвольте мне сказать это для удовольствия :): кажется, что многие разработчики приложили много усилий из-за того, что отсутствовала функция в операционной системе, если потоки были дешевыми для начала. асинхронных библиотек не понадобилось бы.

Обработка ошибок и необязательные значения обрабатываются с несколькими возвращаемыми значениями из функции. Оказывается, ошибки и ситуации, когда значения возвращаются необязательно, в большинстве случаев могут быть обработаны прямо на месте. Возможность переноса необязательных значений часто используется неправильно, что приводит к сложности. И нет, множественные возвращаемые значения не моделируются путем передачи объекта-оболочки и снижения производительности. Это делается путем копирования значений непосредственно в системный стек, как и было задумано Богом. У авторов Golang хватает смелости выйти и работать с системой напрямую, в отличие от других языков, которые застряли на JVM и не могут легко выполнять такие оптимизации. Итак, теперь мы подошли к Map[Key, Value] или в терминах golang map[Key]value. Это намного проще.

Говоря о стеке, стеки автоматически растут по мере использования! И мы приняли за факт, что стеки всегда были фиксированного размера! Это невероятная особенность. В отличие от кучи, стек более непосредственно связан со структурой программы, и это имеет последствия для gc. Очистка стека от мусора — супер-супердешево. С автоматически растущими стеками в стеке может быть размещено больше материала. Действительно, golang ускользает от анализа и пытается выделить в стеке все, что может. Затраты на сборку мусора в основном зависят от количества объектов, а не от их размера, размещение небольших объектов в стеке — это огромная победа.

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

Утиная печать с интерфейсами Go очень хороша. Сначала я очень настороженно относился к этому, но мне это очень понравилось. Основная причина, по которой мне не нравился утиный набор текста в scala, заключалась в связанных с этим затратах времени выполнения. Go не требует затрат времени выполнения. Я большой сторонник принципа разделения интерфейсов, и его конечное использование сводится к объединению интерфейсов с одной функцией. Препятствием для выполнения этого на практике является вся необходимая церемония, и в какой-то момент она также может стать немного шумной. Go убирает весь шум. Фактически каждый объект автоматически определяет все интерфейсы со всеми комбинациями методов, которые он реализует. Реализация интерфейса — это всего лишь вопрос реализации метода с правильной сигнатурой. Практически идеально. Единственный недостаток, который я вижу, заключается в том, что найти всех реализаторов интерфейса непросто, вы не можете просто найти имя интерфейса. Поиск имени функции со всеми типами параметров сложнее. На практике, хотя в большинстве случаев работает поиск имени функции. Плюс в Go уже есть инструменты для этого. Это подводит меня к следующему моменту: Go действительно упрощает реализацию инструментов.

Go поставляется с отличным парсером для языка Go, который можно использовать для создания инструментов. TBH Я сам не пробовал создавать подобные инструменты, но изобилие таких инструментов, похоже, подразумевает, что это действительно очень просто. Я сравниваю это со временем, когда я реализовал инструмент перехода к определению для Scala. О, сколько мне пришлось пройти, чтобы понять, как использовать анализатор scala. Очень мало документации. Это еще одна вещь, которую я обнаружил, когда в документации по сложным вещам часто отсутствует документация. Возможно, это потому, что нужно так много документировать. Конечно, помогает то, что Go такой простой язык, написание синтаксического анализатора, работа с этим синтаксическим анализатором и т. д. было бы намного проще. Меньше действительно больше. Это открывает удивительные возможности, такие как автоматическое изменение кода при нарушении совместимости API и т. д. Это может показаться неочевидным, но это чрезвычайно полезная возможность. Многие инновации сдерживаются тем, что существующий код нельзя изменить, но если бы изменение существующего кода было автоматическим, это было бы намного проще.

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

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

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

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