✍️ Беспощадные строки в Go. Часть 4
Вернёмся к нашим любимым строкам.
В прошлый раз мы разобрались как работают слайсы в Го, а теперь давайте вернёмся к утверждению, что «строки — это слайсы байт».
Возьмём строку в разных представлениях (в байтах и рунах) и попробуем её изменить.
А если изменить что-то в строке?
Компилятор ругается на присваивание элемента в строке. Т.е. не такой уж это и слайс?
На этот вопрос на поможет ответить понимание самой внутренней структуры слайса и строки, посмотрим на неё при помощи рефлексии:
Видно, что у настоящих слайсов есть ёмкость (Cap), а у строки — нет.
На самом деле строки являются неизменяемыми слайсами. А при конвертации в слайсы байт и рун происходит копирование данных под капотом. Это видно из поля Data, в котором собственно и хранится ссылка на первый элемент массива данных, на котором строится слайс.
Вернёмся к нашим любимым строкам.
В прошлый раз мы разобрались как работают слайсы в Го, а теперь давайте вернёмся к утверждению, что «строки — это слайсы байт».
Возьмём строку в разных представлениях (в байтах и рунах) и попробуем её изменить.
s := "hello"
b := []byte(s)
r := []rune(s)
b[0] = 98 // 'b'
r[0] = 'r'
fmt.Printf("%s %s %s\n", s, string(b), string(r))
>> hello bello rello
А если изменить что-то в строке?
s[0] = "s"
>> cannot assign to s[0]
>> Go build failed.
Компилятор ругается на присваивание элемента в строке. Т.е. не такой уж это и слайс?
На этот вопрос на поможет ответить понимание самой внутренней структуры слайса и строки, посмотрим на неё при помощи рефлексии:
sHeader := (*reflect.StringHeader)(unsafe.Pointer(&s))
bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&r))
>> &reflect.StringHeader{Data:0x4c1fd1, Len:5}
>> &reflect.SliceHeader{Data:0xc00002c008, Len:5, Cap:8}
>> &reflect.SliceHeader{Data:0xc000014020, Len:5, Cap:8}
Видно, что у настоящих слайсов есть ёмкость (Cap), а у строки — нет.
На самом деле строки являются неизменяемыми слайсами. А при конвертации в слайсы байт и рун происходит копирование данных под капотом. Это видно из поля Data, в котором собственно и хранится ссылка на первый элемент массива данных, на котором строится слайс.
🌶 Для тех кто любит острее
Вышло как-то скучно, поэтому добавим перчика.
На автомобиле с механической коробкой передач можно ехать быстрее и с меньшем расходом топлива, чем на таком же с автоматом. Но для этого нужно две вещи:
1. понимать, как работает коробка передач и двигатель,
2. иметь возможность переключиться на ручную коробку.
В Го такая возможность есть. И если вам нужно всё-таки сделать изменяемую строку, то вот она:
Всё честно, это строка. А теперь следите за руками:
Строка и слайс байт лежат в памяти в одном месте, никакого копирования.
Внимание! Так делать очень опасно и не рекомендуется. Пакет unsafe отбрасывает систему типов Го и лезет напрямую в память. Это может приводить к очень страшным рантайм ошибкам.
Например, если подобный трюк попытаться провернуть в обратную сторону, то ничего не выйдет:
Без понимания того, как работает коробка передач, лучше ездить на автомате ;)
Вышло как-то скучно, поэтому добавим перчика.
На автомобиле с механической коробкой передач можно ехать быстрее и с меньшем расходом топлива, чем на таком же с автоматом. Но для этого нужно две вещи:
1. понимать, как работает коробка передач и двигатель,
2. иметь возможность переключиться на ручную коробку.
В Го такая возможность есть. И если вам нужно всё-таки сделать изменяемую строку, то вот она:
b := []byte{'h', 'e', 'l', 'l', 'o'}
s := *(*string)(unsafe.Pointer(&b))
fmt.Printf("%T %s\n", s)
>> string hello
Всё честно, это строка. А теперь следите за руками:
b[0] = 's'
fmt.Println(s)
>> sello
Строка и слайс байт лежат в памяти в одном месте, никакого копирования.
fmt.Printf("%#v\n", (*reflect.SliceHeader)(unsafe.Pointer(&b)).Data)
fmt.Printf("%#v\n", (*reflect.StringHeader)(unsafe.Pointer(&s)).Data)
>> 0xc00002c008
>> 0xc00002c008
Внимание! Так делать очень опасно и не рекомендуется. Пакет unsafe отбрасывает систему типов Го и лезет напрямую в память. Это может приводить к очень страшным рантайм ошибкам.
Например, если подобный трюк попытаться провернуть в обратную сторону, то ничего не выйдет:
s = "hello"
b = *(*[]byte)(unsafe.Pointer(&s))
b[0] = 's'
>> unexpected fault address 0x4c1db5
>> fatal error: fault
Без понимания того, как работает коробка передач, лучше ездить на автомате ;)
✍️ Беспощадные строки. Финал
Кажется, что в этой истории мы ушли сильно далеко от обещанной темы канала — языке программирования, как продукте. Но это только на первый взгляд.
В этом коротком, но познавательном сериале про строки и слайсы мы попытались показать их с разных сторон, поверхностно раскрыв некоторые особенности. А из результатов нашего интерактива следует, что не всем такие особенности по душе :)
Но как отмечают создатели языка, на продумывание концепции слайсов ушло больше года.
Как же так?
Больше года думали, а на первый взгляд кажется, что придумали что-то странное.
Дизайн основных примитивов языка — это фича. Причём у этой «фичи» сразу несколько важных особенностей:
* это самые часто используемые элементы языка,
* сложно сделать что-то слишком выходящее за рамки привычного опыта пользователя,
* дизайн напрямую влияет на перфоманс языка.
Поэтому любые решения всегда будут компромиссом. Между эффективностью, предсказуемостью, понятностью и удобством. При этом в случае языка программирования у вас нет шанса на эксперименты, потому что это противоречит другой важной фиче — стабильности и поступательности в развитии. Основные концепции и дизайн строятся на опыте создателей языка, так было сделано и в случае Го.
Кажется, что в этой истории мы ушли сильно далеко от обещанной темы канала — языке программирования, как продукте. Но это только на первый взгляд.
В этом коротком, но познавательном сериале про строки и слайсы мы попытались показать их с разных сторон, поверхностно раскрыв некоторые особенности. А из результатов нашего интерактива следует, что не всем такие особенности по душе :)
Но как отмечают создатели языка, на продумывание концепции слайсов ушло больше года.
Как же так?
Больше года думали, а на первый взгляд кажется, что придумали что-то странное.
Дизайн основных примитивов языка — это фича. Причём у этой «фичи» сразу несколько важных особенностей:
* это самые часто используемые элементы языка,
* сложно сделать что-то слишком выходящее за рамки привычного опыта пользователя,
* дизайн напрямую влияет на перфоманс языка.
Поэтому любые решения всегда будут компромиссом. Между эффективностью, предсказуемостью, понятностью и удобством. При этом в случае языка программирования у вас нет шанса на эксперименты, потому что это противоречит другой важной фиче — стабильности и поступательности в развитии. Основные концепции и дизайн строятся на опыте создателей языка, так было сделано и в случае Го.
🎁 А что в комплекте?
Язык программирования — это уже не просто синтаксис + компилятор. Для повседневной разработки важны инструменты и библиотеки. Обычно эта обвязка вырастает из сформировавшегося вокруг языка комьюнити. Но что делать, когда ты только выходишь на рынок? Это типичная проблема платформенной бизнес-модели (можно считать её проблемой курицы и яйца). Платформе нужны пользователи и поставщики, но поставщики не придут, пока не будет пользователей и наоборот. Тоже самое и с языком — комьюнити растёт вокруг удобных инструментов и библиотек.
И тут очень важна «стандартная комплектация» — набор инструментов и библиотек, которые уже идут в комплекте. Вот и посмотрим на них.
Начнем с gofmt, которая уже упоминалась в канале. Кажется, что это очень простая идея — сделать форматер языка, но она оказалась довольно сильной. В Го полностью отсутствуют холивары о том, какая длина строки верная, где логичнее ставить переносы и отсупы, т.к. сразу был один верный сттандарт, идущий из коробки, который всем ОК.
Это привело к тому, что весь код на Го, даже который ты видишь в первый раз переключившись на внутренности используемой библиотеки, тебе знаком и понятен. Ты сразу визуально понимаешь что и где происходит. Даже горячо обсуждаемые конструкции
оказываются полезными — они делят код на функциональные блоки,
которые автоматом вычленяются мозгом при первом взгляде на код.
Т.е. простая идея встроенного форматера сделала весь код Го скучным и одноликим, но быстро поняным и знакомым.
Язык программирования — это уже не просто синтаксис + компилятор. Для повседневной разработки важны инструменты и библиотеки. Обычно эта обвязка вырастает из сформировавшегося вокруг языка комьюнити. Но что делать, когда ты только выходишь на рынок? Это типичная проблема платформенной бизнес-модели (можно считать её проблемой курицы и яйца). Платформе нужны пользователи и поставщики, но поставщики не придут, пока не будет пользователей и наоборот. Тоже самое и с языком — комьюнити растёт вокруг удобных инструментов и библиотек.
И тут очень важна «стандартная комплектация» — набор инструментов и библиотек, которые уже идут в комплекте. Вот и посмотрим на них.
Начнем с gofmt, которая уже упоминалась в канале. Кажется, что это очень простая идея — сделать форматер языка, но она оказалась довольно сильной. В Го полностью отсутствуют холивары о том, какая длина строки верная, где логичнее ставить переносы и отсупы, т.к. сразу был один верный сттандарт, идущий из коробки, который всем ОК.
Это привело к тому, что весь код на Го, даже который ты видишь в первый раз переключившись на внутренности используемой библиотеки, тебе знаком и понятен. Ты сразу визуально понимаешь что и где происходит. Даже горячо обсуждаемые конструкции
if err != nil {
return err
}
оказываются полезными — они делят код на функциональные блоки,
→ что-то сделали
→ возможно получили ошибку
→ что-то сделали с ошибкой
которые автоматом вычленяются мозгом при первом взгляде на код.
Т.е. простая идея встроенного форматера сделала весь код Го скучным и одноликим, но быстро поняным и знакомым.
blog.golang.org
go fmt your code - The Go Blog
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.
О, если бы понятность кода достигалась единообразным форматированием 🙂
📜 Главный инструмент
Самое время для пятничного поста.
Результаты последнего опроса Golang-разработчиков навели меня на идею небольшого сравнения.
Для меня было удивительно то, что в 2019 году 14% разработчиков на Го используют Vim в качестве предпочитаемого редактора.
Ок, а как там у других?
* Rust — 23,6%, ну вроде объяснимо
* Golang — 14%
* JS — 13%, кто эти люди?
* Python — 9%
* Scala — <7,4%
* и наконец C++ — 7%
Раз такое дело, вот прекрасная статья про Vim.
Самое время для пятничного поста.
Результаты последнего опроса Golang-разработчиков навели меня на идею небольшого сравнения.
Для меня было удивительно то, что в 2019 году 14% разработчиков на Го используют Vim в качестве предпочитаемого редактора.
Ок, а как там у других?
* Rust — 23,6%, ну вроде объяснимо
* Golang — 14%
* JS — 13%, кто эти люди?
* Python — 9%
* Scala — <7,4%
* и наконец C++ — 7%
Раз такое дело, вот прекрасная статья про Vim.
Цитата:
Сколько понадобится времени
Если следовать советам выше и постоянно правильно практиковать вим, то время от «любое действие занимает минуту» до «могу неспешно работать периодически подсматривая в документацию» займет около месяца. Дальше процесс пойдет легче, но останавливаться в развитии на этом этапе нельзя. Оттачивание всех необходимых навыков может занять и год. Но не стоит пугаться. В любом случае через месяц-два вы сможете вполне сносно работать.
Сколько понадобится времени
Если следовать советам выше и постоянно правильно практиковать вим, то время от «любое действие занимает минуту» до «могу неспешно работать периодически подсматривая в документацию» займет около месяца. Дальше процесс пойдет легче, но останавливаться в развитии на этом этапе нельзя. Оттачивание всех необходимых навыков может занять и год. Но не стоит пугаться. В любом случае через месяц-два вы сможете вполне сносно работать.
Что вы думаете о Vim?
Anonymous Poll
16%
Только в нём и пишу
48%
Иногда ОК
14%
Испытываю стойкое отвращение
22%
Никак не могу выйти 😕
💣 Письма в редакцию
Читатель Максим интересуется: как же разработчики Го могут жить без стримов? Stream — понятие из java-мира. Речь идёт о map/filter/reduce подходах из функционального программирования.
Поговаривают, что именно Sequences (аналог джава стримов) помогли Котлину захватить умы андроид-разработчиков, застрявших на 7й джаве в то время.
Хотя в Го и существуют примеры подобных библиотек от комьюнити, но они не могут выглядеть достаточно красиво и удобно из-за отсутствия необходимых возможностей на уровне языка: дженериков и лямбда-функций. Роб Пайк высказал свое мнение по поводу таких интерфейсов довольно давно — пользуйтесь циклами и не парьтесь.
Согласно опросу разработчиков Го 2019 года — фичи функциональных языков (довольно обобщенно) находятся на третьем месте по желаниям разработчиков, сразу после наиболее обсуждаемых дженериков и обработки ошибок.
А что думаете вы? Пользуетесь чем-то похожим в ежедневной практике?
Читатель Максим интересуется: как же разработчики Го могут жить без стримов? Stream — понятие из java-мира. Речь идёт о map/filter/reduce подходах из функционального программирования.
Поговаривают, что именно Sequences (аналог джава стримов) помогли Котлину захватить умы андроид-разработчиков, застрявших на 7й джаве в то время.
Хотя в Го и существуют примеры подобных библиотек от комьюнити, но они не могут выглядеть достаточно красиво и удобно из-за отсутствия необходимых возможностей на уровне языка: дженериков и лямбда-функций. Роб Пайк высказал свое мнение по поводу таких интерфейсов довольно давно — пользуйтесь циклами и не парьтесь.
Согласно опросу разработчиков Го 2019 года — фичи функциональных языков (довольно обобщенно) находятся на третьем месте по желаниям разработчиков, сразу после наиболее обсуждаемых дженериков и обработки ошибок.
А что думаете вы? Пользуетесь чем-то похожим в ежедневной практике?
Что думаете?
Anonymous Poll
17%
Жить без этого не могу
38%
Бывает удобно
29%
Пользуюсь циклами и не парюсь
16%
В первый раз слышу
Документация!
С чем у Go действительно всё классно — так это с документацией.
1️⃣ A Tour of Go — интерактивное введение в язык.
2️⃣ Effective Go — книга о том, как писать идиоматичный код на Go.
3️⃣ Ссылки из документации на исходники каждой функции стандартной библиотеки (например, Max).
Для сравнения я взял топ-10 языков из StackOverflow Developer Survey и посмотрел, как у них дела с этими тремя пунктами:
1. JavaScript. У языка даже своего сайта нет ツ Есть миллион обучалок, но единого авторитетного источника — нет. Документация по языку есть только благодаря Мозилле. Ссылок на исходники нет, конечно. Зато по каждой функции есть интерактивный пример, это отлично.
2. SQL. Всё как у JS, только ещё хуже — в мире SQL не нашлось своей Мозиллы, которая бы сделала документацию с примерами. Где свой SQL получали, там и ищите.
3. Python. Первый язык в топе с собственным сайтом. Есть ссылка Get Started, но дальше разбегаются глаза — что выбрать? Вместо рекомендованной авторами базовой обучалки вываливают на новичка простыню ссылок. Официальный туториал найти можно только каким-то нетривиальным способом. Интерактива нет. Незачёт.
Исходников отдельных функций нет, исходники модулей — только если они написаны на Python (например, для heapq исходники есть, а для itertools — нет)
4. Java. Язык принадлежит компании Оракл, этим всё сказано. Заходите на официальный сайт посмеяться.
5. Bash. Не ожидал, но у него есть сайт и документация. Спасибо и на том.
6. С#. Первый язык родом из нового тысячелетия в списке. Есть интерактивный туториал, тур по языку и руководство разработчика. Неплохо! В документации есть интерактивные примеры, а вот ссылок на исходники нет.
7. PHP. Первое, что встречает на официальном сайте — «Please DO NOT use this version in production, it is an early test version». Спасибо, да. Я бы вообще сделал DO NOT use this in production официальным слоганом языка. Кхм, пардон, отвлёкся. Есть неинтерактивная обучалка, неплохая документация со статическими примерами и комментариями сообщества, и, неожиданно — аж целая книга PHP at the Core: A Hacker's Guide.
8. TypeScript. Новейший язык, 2012 года выпуска. Несколько введений, местами интерактивные. Подробный учебник, который начинается как интерактивный, но быстро превращается в статический (wtf?). Ссылок на исходники нет, но они особо и не нужны — у тайп-скрипта нет собственной стандартной библиотеки.
9. C++. Есть интерактивный тур по языку! Шучу, конечно. Тур выложен в PDF, что тут скажешь. Зато есть Core Guidelines о том, как писать правильный код.
10. C. Сайта нет, ничего нет, населена роботами.
Счёт 10-0 в пользу Go.
С чем у Go действительно всё классно — так это с документацией.
1️⃣ A Tour of Go — интерактивное введение в язык.
2️⃣ Effective Go — книга о том, как писать идиоматичный код на Go.
3️⃣ Ссылки из документации на исходники каждой функции стандартной библиотеки (например, Max).
Для сравнения я взял топ-10 языков из StackOverflow Developer Survey и посмотрел, как у них дела с этими тремя пунктами:
1. JavaScript. У языка даже своего сайта нет ツ Есть миллион обучалок, но единого авторитетного источника — нет. Документация по языку есть только благодаря Мозилле. Ссылок на исходники нет, конечно. Зато по каждой функции есть интерактивный пример, это отлично.
2. SQL. Всё как у JS, только ещё хуже — в мире SQL не нашлось своей Мозиллы, которая бы сделала документацию с примерами. Где свой SQL получали, там и ищите.
3. Python. Первый язык в топе с собственным сайтом. Есть ссылка Get Started, но дальше разбегаются глаза — что выбрать? Вместо рекомендованной авторами базовой обучалки вываливают на новичка простыню ссылок. Официальный туториал найти можно только каким-то нетривиальным способом. Интерактива нет. Незачёт.
Исходников отдельных функций нет, исходники модулей — только если они написаны на Python (например, для heapq исходники есть, а для itertools — нет)
4. Java. Язык принадлежит компании Оракл, этим всё сказано. Заходите на официальный сайт посмеяться.
5. Bash. Не ожидал, но у него есть сайт и документация. Спасибо и на том.
6. С#. Первый язык родом из нового тысячелетия в списке. Есть интерактивный туториал, тур по языку и руководство разработчика. Неплохо! В документации есть интерактивные примеры, а вот ссылок на исходники нет.
7. PHP. Первое, что встречает на официальном сайте — «Please DO NOT use this version in production, it is an early test version». Спасибо, да. Я бы вообще сделал DO NOT use this in production официальным слоганом языка. Кхм, пардон, отвлёкся. Есть неинтерактивная обучалка, неплохая документация со статическими примерами и комментариями сообщества, и, неожиданно — аж целая книга PHP at the Core: A Hacker's Guide.
8. TypeScript. Новейший язык, 2012 года выпуска. Несколько введений, местами интерактивные. Подробный учебник, который начинается как интерактивный, но быстро превращается в статический (wtf?). Ссылок на исходники нет, но они особо и не нужны — у тайп-скрипта нет собственной стандартной библиотеки.
9. C++. Есть интерактивный тур по языку! Шучу, конечно. Тур выложен в PDF, что тут скажешь. Зато есть Core Guidelines о том, как писать правильный код.
10. C. Сайта нет, ничего нет, населена роботами.
Счёт 10-0 в пользу Go.
🌈 Дженерики в Го — история одной фичи
Три главные фичи неустанно обсуждаются в го-комьюнити с начала времён:
* нормальное управление зависимостями,
* дженерики,
* обработка ошибок.
Начиная с Go 1.11, первый вопрос был решён уже в бета-версии, а в релиз Go 1.13 вошла окончательная версия Go Modules — решение всех проблем, связанных с управлением зависимостями и публикацией модулей.
Но вот остальные два вопроса продолжают быть «самыми желанными фичами». Дженерики — лидер с большим отрывом, начиная с 2016 года. В опросе 2019 года за них голосует 79% пользователей! Это невероятный результат для честного голосования (хотя, кто его знает).
Так в чём же собственно проблема? Почему просто нельзя взять и сделать?
Чтобы лучше разобраться в ситуации, пробежимся по основным вехам:
Еще в 2009 году Russ Cox написал от дилемме дженериков:
do you want slow programmers, slow compilers and bloated binaries, or slow execution times
В 2011 году появляется первый пропосал на гитхабе от Ian Taylor — тут следует добавить, что он до сих пор продолжает участвовать в дизайне дженериков (почти 10 лет!).
В 2015 Russ Cox уже не так консервативен и говорит, что просто нужна ещё одна итерация — это чисто техническая проблема.
И вот в 2019 году, спустя почти 10 лет раздумий, появляется огромный черновик дизайна дженериков, который шокирует сообщество. Это невероятное усложнение почти на пустом месте. Твиттер наполняется шутками про то, что контракты — это новые интерфейсы.
Горячее обсуждение внутри комьюнити приводит к тому, что дизайн сильно упрощается и через год, в июне 2020, публикуется обновлённая версия, в которой не вводится дополнительных сущностей, а просто элегантно расширяются прекрасные Го интерфейсы.
Во всей этой истории нам, как продактам, интересно две вещи:
* как обсуждение внутри открытого комьюнити позволяет отследить эволюцию идей и как пользователи могут влиять как в хорошую, так и в плохую сторону,
* как разработчики Го представляют настолько сложную фичу сообществу.
Эти вопросы мы и разберём в следующих постах.
Три главные фичи неустанно обсуждаются в го-комьюнити с начала времён:
* нормальное управление зависимостями,
* дженерики,
* обработка ошибок.
Начиная с Go 1.11, первый вопрос был решён уже в бета-версии, а в релиз Go 1.13 вошла окончательная версия Go Modules — решение всех проблем, связанных с управлением зависимостями и публикацией модулей.
Но вот остальные два вопроса продолжают быть «самыми желанными фичами». Дженерики — лидер с большим отрывом, начиная с 2016 года. В опросе 2019 года за них голосует 79% пользователей! Это невероятный результат для честного голосования (хотя, кто его знает).
Так в чём же собственно проблема? Почему просто нельзя взять и сделать?
Чтобы лучше разобраться в ситуации, пробежимся по основным вехам:
Еще в 2009 году Russ Cox написал от дилемме дженериков:
do you want slow programmers, slow compilers and bloated binaries, or slow execution times
В 2011 году появляется первый пропосал на гитхабе от Ian Taylor — тут следует добавить, что он до сих пор продолжает участвовать в дизайне дженериков (почти 10 лет!).
В 2015 Russ Cox уже не так консервативен и говорит, что просто нужна ещё одна итерация — это чисто техническая проблема.
И вот в 2019 году, спустя почти 10 лет раздумий, появляется огромный черновик дизайна дженериков, который шокирует сообщество. Это невероятное усложнение почти на пустом месте. Твиттер наполняется шутками про то, что контракты — это новые интерфейсы.
Горячее обсуждение внутри комьюнити приводит к тому, что дизайн сильно упрощается и через год, в июне 2020, публикуется обновлённая версия, в которой не вводится дополнительных сущностей, а просто элегантно расширяются прекрасные Го интерфейсы.
Во всей этой истории нам, как продактам, интересно две вещи:
* как обсуждение внутри открытого комьюнити позволяет отследить эволюцию идей и как пользователи могут влиять как в хорошую, так и в плохую сторону,
* как разработчики Го представляют настолько сложную фичу сообществу.
Эти вопросы мы и разберём в следующих постах.
🌈 Дженерики в Го — как показать сообществу сложную фичу
Как видно из предыдущего поста, тональность обсуждения темы дженериков в Го со временем изменялась. Мы знаем из статьи Роба Пайка, что язык задумывался, как инженерное решение для больших проектов. Он должен быть простым и быстрым со строго поступательным развитием — у не должно быть резких и обратно несовместимых изменений в дизайне. Такие тезисы значительно усложняют добавление новых сложных фич.
Если посмотреть на опросы пользователей 2016, 2017 и 2018 года, то будет виден явный рост доли людей, которые не пользуются языком из-за недостатка нужных фич: 11%, 19%, 22%, соответственно. В топ-3 этих фич входили и дженерики. Их не хватает пользователям для построение более сложных абстракций.
И тут получается довольно сложное противопоставление. С одной стороны, у языка уже есть активное комьюнити, в котором ценятся основные его плюсы: простота и скорость. Для них любые усложнения — боль и страдания, а без более сложных абстракций можно и обойтись. Но в какой-то момент рост этих пользователей замедляется и становится видна часть новых ребят, которым не хватает важных фич для использования продукта — для них это блок-фактор. И получаем мы продуктовую задачу: как растить продукт так, чтобы всем было хорошо. Единственный правильный ответ — никак. Всё равно этот путь пойдет через компромиссы. Но его можно и нужно делать более мягким.
Что мы видим дальше?
В 2019 году вместе с драфтом-дизайна дженериков выходит еще и пост (а также и видео на GopherCon от Ian Taylor) с подробным разбором того, зачем же все-таки нужны дженерики, в чём их польза для языка. Это информационная поддержка продуктового развития, начинаем доносить будущую пользу пользователям.
Далее идёт бурное и открытое обсуждение первого дизайна в самой активной части комьюнити. Обсуждение значительно влияет на вторую версию дизайна, тем самым завлекая сообщество в решение этой проблемы.
Потом мы видим результаты опроса разработчиков 2019 года, в котором уже 79% разработчиков не могут жить без дженериков. Неплохой рост с 22%, правда? Теперь это уже явное желание комьюнити!
Вместе со вторым дизайном в 2020 году выходит в свет версия языка и плейграунд (!) с его поддержкой. Этот шаг вовлекает в обсуждение новых пользователей, которые раньше не находили сил погрузиться в сложное описание. А теперь то уже можно всё потрогать! Появляется большое количество статей с примерами на плейграунде от разных ребят, распространяя фичу еще шире.
Дженерики появятся в Го не раньше, чем через год. Но к тому времени информационная поддержка сработает уже так хорошо, что большинство пользователей будет знакомо с фичей и никакого негатива это не вызовет.
Как видно из предыдущего поста, тональность обсуждения темы дженериков в Го со временем изменялась. Мы знаем из статьи Роба Пайка, что язык задумывался, как инженерное решение для больших проектов. Он должен быть простым и быстрым со строго поступательным развитием — у не должно быть резких и обратно несовместимых изменений в дизайне. Такие тезисы значительно усложняют добавление новых сложных фич.
Если посмотреть на опросы пользователей 2016, 2017 и 2018 года, то будет виден явный рост доли людей, которые не пользуются языком из-за недостатка нужных фич: 11%, 19%, 22%, соответственно. В топ-3 этих фич входили и дженерики. Их не хватает пользователям для построение более сложных абстракций.
И тут получается довольно сложное противопоставление. С одной стороны, у языка уже есть активное комьюнити, в котором ценятся основные его плюсы: простота и скорость. Для них любые усложнения — боль и страдания, а без более сложных абстракций можно и обойтись. Но в какой-то момент рост этих пользователей замедляется и становится видна часть новых ребят, которым не хватает важных фич для использования продукта — для них это блок-фактор. И получаем мы продуктовую задачу: как растить продукт так, чтобы всем было хорошо. Единственный правильный ответ — никак. Всё равно этот путь пойдет через компромиссы. Но его можно и нужно делать более мягким.
Что мы видим дальше?
В 2019 году вместе с драфтом-дизайна дженериков выходит еще и пост (а также и видео на GopherCon от Ian Taylor) с подробным разбором того, зачем же все-таки нужны дженерики, в чём их польза для языка. Это информационная поддержка продуктового развития, начинаем доносить будущую пользу пользователям.
Далее идёт бурное и открытое обсуждение первого дизайна в самой активной части комьюнити. Обсуждение значительно влияет на вторую версию дизайна, тем самым завлекая сообщество в решение этой проблемы.
Потом мы видим результаты опроса разработчиков 2019 года, в котором уже 79% разработчиков не могут жить без дженериков. Неплохой рост с 22%, правда? Теперь это уже явное желание комьюнити!
Вместе со вторым дизайном в 2020 году выходит в свет версия языка и плейграунд (!) с его поддержкой. Этот шаг вовлекает в обсуждение новых пользователей, которые раньше не находили сил погрузиться в сложное описание. А теперь то уже можно всё потрогать! Появляется большое количество статей с примерами на плейграунде от разных ребят, распространяя фичу еще шире.
Дженерики появятся в Го не раньше, чем через год. Но к тому времени информационная поддержка сработает уже так хорошо, что большинство пользователей будет знакомо с фичей и никакого негатива это не вызовет.
blog.golang.org
Go 2018 Survey Results - The Go Blog
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.
🦄 Фича х10
В любом крутом продукте должно быть что-то х10 ко всем аналогам, так завещают все гуру продуктологии. В 10 раз дешевле, в 10 раз быстрее, в 10 раз удобнее. Это и есть тот самый дизрапт, являющийся граалью любого стартап питча. Простите за много грязных слов, но мы же пытаемся тут походить на серьезных продактов, ведь так?
Есть ли что-то подобное в языке программирования Го как продукте?
Не буду тянуть, ответ есть и он очень простой. Как выглядит вызов синхронной функции, печатающей "Hello, world!":
Окей, а как две неблокирующие функциии могут взаимодействовать с общими данными? Будет же блокировка! Нет, если есть примауомо Канал:
Это и есть фича х10. Давайте теперь разбираться что тут к чему.
В любом крутом продукте должно быть что-то х10 ко всем аналогам, так завещают все гуру продуктологии. В 10 раз дешевле, в 10 раз быстрее, в 10 раз удобнее. Это и есть тот самый дизрапт, являющийся граалью любого стартап питча. Простите за много грязных слов, но мы же пытаемся тут походить на серьезных продактов, ведь так?
Есть ли что-то подобное в языке программирования Го как продукте?
Не буду тянуть, ответ есть и он очень простой. Как выглядит вызов синхронной функции, печатающей "Hello, world!":
func() {
fmt.Println("Hello, world!")
}()
А вызов такой же функции, которая не будет блокировать основной сценарий исполнения? Знакомьтесь, примадонна Горутина:go func(){
fmt.Println("Hello, world!")
}()
Окей, а как две неблокирующие функциии могут взаимодействовать с общими данными? Будет же блокировка! Нет, если есть примауомо Канал:
shareData := make(chan data)
go func(in chan<- data){
doSomeWriteTo(in)
}(shareData)
go func(in <-chan data){
doSomeReadFrom(in)
}(shareData)
Это и есть фича х10. Давайте теперь разбираться что тут к чему.
👩💻 Примадонна Горутина
Что же такого примечательного в запуске неблокирующей функции в Го?
Даже в FAQ Го есть ответ на этот вопрос. Главная особенность в том, что в Го неблокирующая функция запускается в горутине, а не в системном треде. Горутина — абстракция Го рантайма, обеспечивающая себя тремя дополнительными функциями и несколькими килобайтами памяти. Поэтому горутин можно запустить миллионы на обычном бытовом ноутбуке, тогда как тредов лишь тысячи.
Это, конечно, очень круто. Практически невесомая в части ресурсов асинхронность отлично работает в современном сетевом мире, где на каждый шаг бизнес-логики нужно ждать ответа по сети — клиент-серверное взаимодействие, микросервисная архитектура, взаимодействие с базой данных и т.п. Но это все смертная скукота, о которой написаны километры статей.
Знаете, в продукте должен быть шарм. Андрей Бреслав — дизайнер языка Котлин — как-то сказал, что функция в Котлине начинается с ключевого слова
Но вот fun'а от вызова функции в Котлине я ни разу не испытал (признаюсь, не часто и пытался), но я кайфую от вызова горутины. Вы только посмотрите:
Ты вроде просто добавляешь ключевое слово
Что же такого примечательного в запуске неблокирующей функции в Го?
Даже в FAQ Го есть ответ на этот вопрос. Главная особенность в том, что в Го неблокирующая функция запускается в горутине, а не в системном треде. Горутина — абстракция Го рантайма, обеспечивающая себя тремя дополнительными функциями и несколькими килобайтами памяти. Поэтому горутин можно запустить миллионы на обычном бытовом ноутбуке, тогда как тредов лишь тысячи.
Это, конечно, очень круто. Практически невесомая в части ресурсов асинхронность отлично работает в современном сетевом мире, где на каждый шаг бизнес-логики нужно ждать ответа по сети — клиент-серверное взаимодействие, микросервисная архитектура, взаимодействие с базой данных и т.п. Но это все смертная скукота, о которой написаны километры статей.
Знаете, в продукте должен быть шарм. Андрей Бреслав — дизайнер языка Котлин — как-то сказал, что функция в Котлине начинается с ключевого слова
fun
, потому что это fun! И в этом что-то есть.Но вот fun'а от вызова функции в Котлине я ни разу не испытал (признаюсь, не часто и пытался), но я кайфую от вызова горутины. Вы только посмотрите:
go func(){
someWork()
}()
Ты вроде просто добавляешь ключевое слово
go
, а на самом деле рождаешь что-то независимое и самодостаточное. На этом с философией заканчиваем и возвращаемся к скучным техническим деталям.go.dev
Frequently Asked Questions (FAQ) - The Go Programming Language
🥺 Независимость и контроль
Нарассказывал я вам про фичу х10, но не всё так просто. Нельзя просто так взять и сделать понятную асинхронность. Да, в Го всё выглядит неплохо, по сравнению с другими языками (привет, @ohmypy), но всегда есть но. Дальше давайте разбираться уже на примерах, без них тут ничего не объяснить.
Воу! А куда делась то наша так просто запущенная горутина? А она просто не успела выполниться до завершения основной функции. И да, main функция тоже работает внутри горутины.
И тут мы приходим к тому, что любая асинхронность привозит с собой вагон и маленькую тележку дополнительных примитивов синхронизации. Хочешь запускать асинхронные функции? Люби и разбираться, где их потом искать и как останавливать.
В нашем случае поможет
Вроде справились, но и это не всё. У Го здесь есть свой путь, к которому мы и переходим дальше — каналы.
Если вам интересно подробнее поразбираться с асинхронностью в Го, то в
Нарассказывал я вам про фичу х10, но не всё так просто. Нельзя просто так взять и сделать понятную асинхронность. Да, в Го всё выглядит неплохо, по сравнению с другими языками (привет, @ohmypy), но всегда есть но. Дальше давайте разбираться уже на примерах, без них тут ничего не объяснить.
fmt.Println("Hello, Gopher!")
go func() {
time.Sleep(time.Second)
fmt.Println("I'm inside the Goroutine!")
}()
fmt.Println("Buy, Gopher!")
// >> Hello, Gopher!
// >> Buy, Gopher!
Воу! А куда делась то наша так просто запущенная горутина? А она просто не успела выполниться до завершения основной функции. И да, main функция тоже работает внутри горутины.
И тут мы приходим к тому, что любая асинхронность привозит с собой вагон и маленькую тележку дополнительных примитивов синхронизации. Хочешь запускать асинхронные функции? Люби и разбираться, где их потом искать и как останавливать.
В нашем случае поможет
WaitGroup
из стандартного пакета sync
— будем ждать, пока горутина все же отработает.fmt.Println("Hello, Gopher!")
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(time.Second)
fmt.Println("I'm inside the Goroutine!")
}()
wg.Wait()
fmt.Println("Buy, Gopher!")
// >> Hello, Gopher!
// >> I'm inside Goroutine!
// >> Buy, Gopher!
Вроде справились, но и это не всё. У Го здесь есть свой путь, к которому мы и переходим дальше — каналы.
Если вам интересно подробнее поразбираться с асинхронностью в Го, то в
A Tour of Go
есть прекраснейший раздел.Проблемы первого мира! В питоне, если хочешь сделать функцию доступной для синхронного и асинхронного вызова — приходится писать две функции 😐
Go на практике
У этого канала два автора — Миша и Антон. Миша отлично знает Go и активно применяет его на работе, а Антон (это я) ограничивался почитыванием статей и глубокомысленными высказываниями вроде «что за язык, даже дженериков нет», «да вы посмотрите, они символы 'рунами' называют» и «неужели нельзя было сделать как в питоне».
Но бесконечно так продолжаться не могло. Я наконец решил нормально освоить Go, потому что все же что-то такое в нем есть, неуловимо притягательное. И сразу столкнулся с проблемами:
1) Хочется нормальный курс. А то они все сделаны в стиле «сейчас, дети, я два часа буду рассказывать вам, как объявлять переменные». «А вот что такое цикл». «Давайте подойдем поближе и внимательно рассмотрим удивительную конструкцию if-else». Эти ребята вообще в курсе, что некоторые из нас уже умеют программировать?
2) Хочется вменяемых задачек. А не «что вернет эта функция» (где функция представляет собой адовый треш, который никто в здравом уме никогда не напишет).
3) Одному скучно, хочется сообщников и сообщества. Обмениваться опытом и замечать то, что сам пропустил. Обсуждать подходы и практики. Да те же задачки решать.
Пока я думал, что с этим делать — наткнулся на концепцию «learn in public». В результате появился курс «Go на практике». Если вы как я, в основном знакомы с Go по статьям в интернете, или знали, но подзабыли, или просто любите решать задачки — присоединяйтесь!
https://stepik.org/a/96832
У этого канала два автора — Миша и Антон. Миша отлично знает Go и активно применяет его на работе, а Антон (это я) ограничивался почитыванием статей и глубокомысленными высказываниями вроде «что за язык, даже дженериков нет», «да вы посмотрите, они символы 'рунами' называют» и «неужели нельзя было сделать как в питоне».
Но бесконечно так продолжаться не могло. Я наконец решил нормально освоить Go, потому что все же что-то такое в нем есть, неуловимо притягательное. И сразу столкнулся с проблемами:
1) Хочется нормальный курс. А то они все сделаны в стиле «сейчас, дети, я два часа буду рассказывать вам, как объявлять переменные». «А вот что такое цикл». «Давайте подойдем поближе и внимательно рассмотрим удивительную конструкцию if-else». Эти ребята вообще в курсе, что некоторые из нас уже умеют программировать?
2) Хочется вменяемых задачек. А не «что вернет эта функция» (где функция представляет собой адовый треш, который никто в здравом уме никогда не напишет).
3) Одному скучно, хочется сообщников и сообщества. Обмениваться опытом и замечать то, что сам пропустил. Обсуждать подходы и практики. Да те же задачки решать.
Пока я думал, что с этим делать — наткнулся на концепцию «learn in public». В результате появился курс «Go на практике». Если вы как я, в основном знакомы с Go по статьям в интернете, или знали, но подзабыли, или просто любите решать задачки — присоединяйтесь!
https://stepik.org/a/96832
Stepik: online education
Thank Go! Golang на практике
Осваиваем Golang на практических задачах. Для опытных разработчиков, которые хотят быстро начать применять Go в работе.
Thank Go!
🥺 Независимость и контроль Нарассказывал я вам про фичу х10, но не всё так просто. Нельзя просто так взять и сделать понятную асинхронность. Да, в Го всё выглядит неплохо, по сравнению с другими языками (привет, @ohmypy), но всегда есть но. Дальше давайте…
🥺 Независимость и контроль – 2
@nalgeon взбудоражил канал своим learn in public. Придётся дописывать недописанные посты 😇
Напомню, мы разбирали ситуацию, когда горутина не успевает выполниться до завершения main горутины. В качестве варианта решения использовали
Но есть и другие способы. Один из них — воспользоваться блокировкой чтения из канала.
Мы создаём канал и ждём из него сообщения в main горутине на 8 строчке. Пока этого не случится она будет заблокирована.
В дочерней горутине мы пишем в этот же канал на 6 строчке. Тем самым main горутина дожидается завершения работы дочерней. Таким образом мы получили полный аналог синхронизации из предыдущего примера. Код в плейграндуе.
Антон, как думаешь, какой из вариантов более Go Way?
@nalgeon взбудоражил канал своим learn in public. Придётся дописывать недописанные посты 😇
Напомню, мы разбирали ситуацию, когда горутина не успевает выполниться до завершения main горутины. В качестве варианта решения использовали
WaitGroup
из пакета sync
.Но есть и другие способы. Один из них — воспользоваться блокировкой чтения из канала.
1 fmt.Println("Hello, Gopher!")
2 done := make(chan struct{})
3 go func() {
4 time.Sleep(time.Second)
5 fmt.Println("I'm inside the Goroutine!")
6 done <- struct{}{}
7 }()
8 <-done
9 fmt.Println("Buy, Gopher!")
10 close(done)
>> Hello, Gopher!
>> I'm inside Goroutine!
>> Buy, Gopher!
Мы создаём канал и ждём из него сообщения в main горутине на 8 строчке. Пока этого не случится она будет заблокирована.
В дочерней горутине мы пишем в этот же канал на 6 строчке. Тем самым main горутина дожидается завершения работы дочерней. Таким образом мы получили полный аналог синхронизации из предыдущего примера. Код в плейграндуе.
Антон, как думаешь, какой из вариантов более Go Way?
Возможности языка
Если говорить о возможностях самого языка Go (а не тулинга или стандартной библиотеки), то вот что я думаю.
Нравится отсутствие явного public/private, скобочек в условиях и точек с запятой. Действительно, зачем синтаксис там, где без него можно обойтись.
Нравится, как сделаны функции. Все отлично — и несколько возвращаемых значений, и анонимные функции, и типы на основе функций, и методы.
Скорее нравится, что у всех файлов в пределах пакета общий scope. Скорее не нравится, что заранее не подумали о модулях и приделали их потом сбоку (да и назвали «модулями», хотя какие они к черту модули).
Нравятся указатели без арифметики. Хороший баланс между возможностями и хрупкостью.
Не нравится iota. Это магия (плохонькая), а Go силен как раз тем, что это язык без магии. Лучше бы честные енумы сделали.
Нравятся defined-типы, они добавляют семантичности коду.
Нравятся интерфейсы. Впервые встречаю интерфейсы здорового человека, очень классно придумано.
Нравится embedding в структурах. Простая концепция, но необычайно мощная (сначала даже не понимаешь, насколько).
Нравится defer. Удобнее и мощнее, чем try-finally.
Не нравятся panic и recover. Даже не сами по себе, а то, что из-за них получилось два разных инструмента работы с ошибками.
Конечно, нравятся горутины и каналы. Но тут вряд ли может быть два мнения ツ
Если говорить о возможностях самого языка Go (а не тулинга или стандартной библиотеки), то вот что я думаю.
Нравится отсутствие явного public/private, скобочек в условиях и точек с запятой. Действительно, зачем синтаксис там, где без него можно обойтись.
Нравится, как сделаны функции. Все отлично — и несколько возвращаемых значений, и анонимные функции, и типы на основе функций, и методы.
Скорее нравится, что у всех файлов в пределах пакета общий scope. Скорее не нравится, что заранее не подумали о модулях и приделали их потом сбоку (да и назвали «модулями», хотя какие они к черту модули).
Нравятся указатели без арифметики. Хороший баланс между возможностями и хрупкостью.
Не нравится iota. Это магия (плохонькая), а Go силен как раз тем, что это язык без магии. Лучше бы честные енумы сделали.
Нравятся defined-типы, они добавляют семантичности коду.
Нравятся интерфейсы. Впервые встречаю интерфейсы здорового человека, очень классно придумано.
Нравится embedding в структурах. Простая концепция, но необычайно мощная (сначала даже не понимаешь, насколько).
Нравится defer. Удобнее и мощнее, чем try-finally.
Не нравятся panic и recover. Даже не сами по себе, а то, что из-за них получилось два разных инструмента работы с ошибками.
Конечно, нравятся горутины и каналы. Но тут вряд ли может быть два мнения ツ