На протяжении последних месяцев я использую Go для имплементаций Proof of Concept (прим.пер.: код для проверки работоспособности идеи) в свободное время, отчасти для изучения самого языка программирования. Программы сами по себе очень просты и не являются целью написания статьи, но сам опыт использования Go заслуживает того, чтобы сказать о нем пару слов. Go обещает быть (прим.пер.: статья написана в 2015) массовым языком для серьезного масштабируемого кода. Язык создан в Google, в котором активно им пользуются. Подведя черту, я искренне считаю, что дизайн языка Go плох для умных программистов.
Создан для слабых программистов?
Go очень просто научиться, настолько просто, что введение заняло у меня один вечер, после чего уже мог продуктивно писать код. Книга по которой я изучал Go называется An Introduction to Programming in Go (перевод), она доступна в сети. Книгу, как и сам исходный код на Go, легко читать, в ней есть хорошие примеры кода, она содержит порядка 150 страниц, которые можно прочесть за раз. Сначала эта простота действует освежающе, особенно в мире программирования, полного переусложненных технологий. Но в итоге рано или поздно возникает мысль: "Так ли это на самом деле?"
Google утверждает, что простота Go — это подкупающая черта, и язык предназначен для максимальной продуктивности в больших командах, но я сомневаюсь в этом. Есть фичи, которых либо недостает, либо они чрезмерно подробны. А все из-за отсутствия доверия к разработчикам, с предположением, что они не в состоянии сделать что-либо правильно. Это стремление к простоте было сознательным решением разработчиков языка и, для того, чтобы полностью понять для чего это было нужно, мы должны понять мотивацию разработчиков и чего они добивались в Go.
Так для чего же он был создан таким простым? Вот пара цитат Роба Пайка (прим.пер.: один из соавторов языка Go):
Ключевой момент здесь, что наши программисты (прим.пер.: гуглеры) не исследователи. Они, как правило, весьма молоды, идут к нам после учебы, возможно изучали Java, или C/C++, или Python. Они не в состоянии понять выдающийся язык, но в то же время мы хотим, чтобы они создавали хорошее ПО. Именно поэтому их язык должен прост им для понимания и изучения.
Он должен быть знакомым, грубо говоря похожим на Си. Программисты работающие в Google рано начинают свою карьеру и в большинстве своем знакомы с процедурными языками, в частности семейства Си. Требование в скорой продуктивности на новом языке программирования означает, что язык не должен быть слишком радикальным.
Что? Так Роб Пайк в сущности говорит, что разработчики в Google не столь хороши, потому они и создали язык для идиотов (прим.пер.: dumbed down), так чтобы они были в состоянии что-то сделать. Что за высокомерный взгляд на собственных коллег? Я всегда считал, что разработчики Google отобраны из самых ярких и лучших на Земле. Конечно они могут справиться с чем-то посложнее?
Артефакты чрезмерной простоты
Быть простым — это достойное стремление в любом дизайне, а попытаться сделать нечто простым трудно. Однако при попытке решить (или даже выразить) сложные задачи, порой необходим сложный инструмент. Сложность и запутанность не лучшие черты языка программирования, но существует золотая середина, при которой в языке возможно создание элегантных абстракций, простых в понимании и использовании.
Не очень выразительный
Из-за стремления к простоте в Go отсутствуют конструкции, которые в остальных языках воспринимаются как что-то естественное. Вначале это может показаться хорошей идеей, но на практике выходит многословный код. Причина этому должна быть очевидна — необходимо, чтобы разработчикам было просто читать чужой код, но на самом деле эти упрощения только вредят читаемости. Сокращения в Go отсутствует: либо много, либо ничего.
К примеру, консольная утилита, которая читает stdin либо файл из аргументов командной строки, будет выглядеть следующим образом:
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
)
func main() {
flag.Parse()
flags := flag.Args()
var text string
var scanner *bufio.Scanner
var err error
if len(flags) > 0 {
file, err := os.Open(flags[0])
if err != nil {
log.Fatal(err)
}
scanner = bufio.NewScanner(file)
} else {
scanner = bufio.NewScanner(os.Stdin)
}
for scanner.Scan() {
text += scanner.Text()
}
err = scanner.Err()
if err != nil {
log.Fatal(err)
}
fmt.Println(text)
}
Хотя и этот код пытается быть как можно более общим, принудительная многословность Go мешает, и в результате решение простой задачи выливается в большой объем кода.
Вот, к примеру, решение той же задачи на D:
import std.stdio, std.array, std.conv;
void main(string[] args)
{
try
{
auto source = args.length > 1 ? File(args[1], "r") : stdin;
auto text = source.byLine.join.to!(string);
writeln(text);
}
catch (Exception ex)
{
writeln(ex.msg);
}
}
И кто теперь более читабельный? Я отдам свой голос D. Его код куда более читаемый, так как он более явно описывает действия. В D используются концепции куда сложнее (прим.пер.: альтернативный вызов функций и шаблоны), чем в примере с Go, но на самом деле нет ничего сложного в том, чтобы разобраться в них.
Ад копирования
Популярное предложение для улучшения Go — это обобщенность. Это хотя бы поможет избежать ненужного копирования кода для поддержки всех типов данных. К примеру, функцию для суммирования списка целых чисел можно реализовать никак иначе, кроме как ее копипастой ее базовой функции для каждого целого типа, другого способа нет:
package main
import "fmt"
func int64Sum(list []int64) (uint64) {
var result int64 = 0
for x := 0; x < len(list); x++ {
result += list[x]
}
return uint64(result)
}
func int32Sum(list []int32) (uint64) {
var result int32 = 0
for x := 0; x < len(list); x++ {
result += list[x]
}
return uint64(result)
}
func int16Sum(list []int16) (uint64) {
var result int16 = 0
for x := 0; x < len(list); x++ {
result += list[x]
}
return uint64(result)
}
func int8Sum(list []int8) (uint64) {
var result int8 = 0
for x := 0; x < len(list); x++ {
result += list[x]
}
return uint64(result)
}
func main() {
list8 := []int8 {1, 2, 3, 4, 5}
list16 := []int16{1, 2, 3, 4, 5}
list32 := []int32{1, 2, 3, 4, 5}
list64 := []int64{1, 2, 3, 4, 5}
fmt.Println(int8Sum(list8))
fmt.Println(int16Sum(list16))
fmt.Println(int32Sum(list32))
fmt.Println(int64Sum(list64))
}
И этот пример даже не работает для знаковых типов. Такой подход полностью нарушает принцип не повторять себя (DRY), один из наиболее известных и очевидных принципов, игнорирование которого является источником многих ошибок. Зачем Go это делает? Это ужасный аспект языка.
Тот же пример на D:
import std.stdio;
import std.algorithm;
void main(string[] args)
{
[1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln;
}
Простое, элегантное и прямо в точку. Здесь используется функция reduce
для шаблонного типа и предиката. Да, это опять же сложнее варианта с Go, но не столь уж сложно для понимания умными программистами. Который из примеров проще поддерживать и легче читать?
Простой обход системы типов
Я полагаю, читая это, программисты Go будут с пеной во рту кричать: "Ты делаешь это не так!". Что же, есть еще один способ сделать обобщенную функцию и типы, но это полностью разрушает систему типов!
Взгляните на этот пример глупого исправления языка для обхода проблемы:
package main
import "fmt"
import "reflect"
func Reduce(in interface{}, memo interface{}, fn func(interface{}, interface{}) interface{}) interface{} {
val := reflect.ValueOf(in)
for i := 0; i < val.Len(); i++ {
memo = fn(val.Index(i).Interface(), memo)
}
return memo
}
func main() {
list := []int{1, 2, 3, 4, 5}
result := Reduce(list, 0, func(val interface{}, memo interface{}) interface{} {
return memo.(int) + val.(int)
})
fmt.Println(result)
}
Эта имплементация Reduce
была позаимствована из статьи Idiomatic generics in Go (прим.пер.: перевод не нашел, буду рад, если поможете с этим). Что же, если это идиоматично, я бы не хотел увидеть не идиоматичный пример. Использование interface{}
— фарс, и в языке он нужен лишь для обхода типизации. Это пустой интерфейс и все типы его реализуют, позволяя полную свободу для всех. Этот стиль программирования до ужаса безобразен, и это еще не все. Для подобных акробатических трюков требуется использовать рефлексию времени выполнения. Даже Робу Пайку не нравятся индивиды, злоупотребляющие этим, о чем он упоминал в одном из своих докладов.
Это мощный инструмент, который должен быть использован с осторожностью. Его следует избегать пока в нем нет строгой необходимости.
Я бы взял шаблоны D вместо этой чепухи. Как кто-то может сказать, что interface{}
более читаем или даже типобезопасен?
Горе управления зависимостями
У Go есть встроенная система зависимостей, построенная поверх популярных хостингов VCS. Поставляемые с Go инструменты знают об этим сервисах и могут скачивать, собирать и устанавливать из них код одним махом. Хотя это и здорово, есть крупная оплошность с версионированием! Да действительно, можно получить исходный код из сервисов вроде github или bitbucket с помощью инструментов Go, но нельзя указать версию. И снова простота в ущерб полезности. Я не в состоянии понять логику подобного решения.
После вопросов о решении этой проблемы, команда разработки Go создала ветку форума, в которой изложили, как они собираются обойти этот вопрос. Их рекомендация была просто однажды скопировать весь репозиторий себе в проект и оставить "как есть". Какого черта они думают? У нас есть потрясающие системы контроля версий с отличным теггированием и поддержкой версий, которые создатели Go игнорируют и просто копируют исходные тексты.
Культурный багаж из Си
По-моему мнению, Go был разработан людьми, которые использовали Си всю свою жизнь и теми, кто не хотел попытаться использовать что-то новое. Язык можно описать как Си с дополнительными колесиками(ориг.: training wheels). В нем нет новых идей, кроме поддержки параллелизма (который, кстати, прекрасен) и это обидно. У вас есть отличная параллельность в едва ли годном к употреблению, хромающем языке.
Еще одна скрипучая проблема в том, что Go — это процедурный язык (подобно тихому ужасу Си). В итоге начинаешь писать код в процедурном стиле, который ощущается архаичным и устаревшим. Я знаю, что объектно-ориентированное программирование — это не серебряная пуля, но было бы здорово иметь возможность абстрагировать детали в типы и обеспечить инкапсуляцию.
Простота для собственной выгоды
Go был разработан, чтобы быть простым и он преуспел в этой цели. Он был написан для слабых программистов, используя в качестве заготовки старый язык. Поставляется он в комплекте с простыми инструментами для выполнения простых вещей. Его просто читать и просто использовать.
Он крайне многословный, невыразительный и плох для умных программистов.
Спасибо mersinvald за правки
Комментарии (316)
wOvAN
11.12.2017 00:04Go меня привлек именно как платформа (нативный код с GC, с весьма простой и зачастую работающей, кросскомпиляцией, без виртуальных машин и прочих рантаймов) а вот по части языка, да претензий много, многое чего хотелось бы нет, многое реализовано очень странно.
Когда хочется написать чтото действиетельно сложное, (пытаясь простыми средствами реализовать сложную конструкцию или недостающую) то обычно простой код может превратиться в очень труднопонимаемый.sheknitrtch
11.12.2017 00:43Попробуйте язык D, который приводится в качестве примера в статье. Нативный код с GC, нет зоопарка разных компиляторов. Я использую его для решения задач из HackerRank.com и мне D показался отличным языком для написания алгоритмов.
mersinvald
11.12.2017 00:51Язык отличный, жаль только, что команда разработки слишком увлечена впиливанием новых фишечек, а не доведением готовых до стабильного релиза.
Компилятор, как и stdlib всё еще имеют баги, которые препятствуют использованию D в продакшне. Увы, это язык скорее "для души" нежели для работы.
Temtaime
11.12.2017 10:49Года 2 назад так и было, сейчас довольно редко можно наткнуться на очень уж критические баги.
mersinvald
11.12.2017 11:11Ну хз, у нас сейчас переписывают на Rust то, что было на D именно из-за нестабильности языка и багов.
Temtaime
11.12.2017 13:31У меня есть крупный проект клиента MMORPG на OpenGL на D.
В начале разработки(4-5 лет назад) постоянно натыкался на баги в компиляторе/стандартной библиотеке. За последний год если только несколько багов в новой функциональности нашёл.
Единственная проблема крупная на данный момент — консервативный GC, который в моём клиенте умудряется течь, чем несказанно портит жизнь.
Главная проблема комьюнити — они любят трепаться о всяких новых технологиях, но никто не хочет тот же самый GC сделать precise.jacob1237
11.12.2017 20:35У Вас клиент наверное под x86 без 64?
Temtaime
11.12.2017 21:50Под x86_64.
jacob1237
12.12.2017 15:07Странно что он течет под x86-64, т.к. по идее проблема "фальшивых" указателей на этой платформе стоит не так остро как на 32 разрядных системах.
Жаль это слышать, надеюсь они наконец-то дотестят и сольют пулл реквест precise gc с мастером.
А сколько примерно объектов в памяти у Вас в среднем? Просто для статистики, хочу лучше понять масштаб проблемы уктечек на стандартном GC
khim
11.12.2017 01:58А как оно будет через 10 лет? Сможете скомпилировать и запустить код, написанный сегодня?
Это один из важгейших вопросов — но я не понимаю что разработчики D вообще про него думают…
Yardanico
11.12.2017 09:24Можете попробовать Nim, там тоже нативный код с опциональным GC, к тому же ведётся работа над деструкторами, кросс-компиляция как в Си (Nim компилируется в Си), никаких виртуальных машин и максимальная производительность (на уровне близком к чистому Си при правильном использовании языка)
antonksa
13.12.2017 00:49Я все пытаюсь перейти на Nim как на основной язык для работы. Мешает только отсутствие развитого комьюнити и отсутствие зоопарка готовых либ, как в питоне, на котором я сейчас пишу.
CodeRush
11.12.2017 01:04Автор не понимает целей и задач Гугла при разработке этого языка, поэтому сетует на то, что Go — это такой «С для дураков», а надо было вместо него внедрять что другое, с нативным ООП, с шаблонами, с выводом типов. D, например, или Rust, или вообще ЯФП какой-нибудь.
Так вот, примерно 90% задач гугла вполне решается на этом самом «С для дураков» без использования кодогенерации, рефлексии, шаблонов, ООП и чего либо еще, и самое главное, что в итоге написанный разработчиками любого уровня код на Go смогут понять любые другие разработчики, даже если они в компании работают сутки и пришли сразу после ВУЗа.
Более того, при уходе всех оригинальных разработчиков из команды проект не придется выбрасывать весь, потому что в нем никто не может разобраться, а получится передать другой команде обычных программистов, и они смогут подхватить его в относительно короткие сроки.
Простота поддержки, простота отладки и поиска ошибок, доступность даже для новичков, и минимальные шансы выстрелить себе в ногу — вот что нужно компаниям уровня Гугла для большей части их кода. А оставшуюся меньшую часть можно писать на чем угодно, хоть на ассемблере, если есть такая необходимость.third112
11.12.2017 02:01примерно 90% задач гугла вполне решается на этом самом «С для дураков»
Откуда такая цифра «90%»? Всякие intelligent personal assistant'ы, беспилотные автомобили и другие AI-проекты, которыми так годится Гугл, входят в эти 90% задач? А Google Chrome?CodeRush
11.12.2017 02:09Грубая оценка по опыту работы в другой большой компании похожего уровня.
Из всего вышеперечисленного только Chrome уже написан, и браться его переписывать на Go с нуля нет никакого смысла, все остальное — почему нет? Для хардкорной математики все равно будут использованы готовые библиотеки на С или Фортране (FFI у языка есть, сопряжение хоть и не без проблем, но работает), а для основной grunt work язык Go вполне подходит. Они на нем теперь даже initramfs пишут, так что я не удивлюсь, если напишут и что-нибудь из ML (AI все же слишком сильный термин, на мой взгляд).third112
11.12.2017 02:20Из всего вышеперечисленного только Chrome уже написан, и браться его переписывать на Go с нуля нет никакого смысла
Помимо смысла: писать хороший GUI без богатого ООП очень трудно, хотя и возможно.
AI все же слишком сильный термин, на мой взгляд
Если assistant по замыслу не просто «болталка», вроде Элизы, то AI там нужен мощнее, чем сегодня доступен. Т.о. существующих библиотек явно недостаточно. Нужны собственные научные разработки. Аналогично про беспилотный автомобиль. (Или есть способ прятать внутри живого пилота :)khim
11.12.2017 03:58Помимо смысла: писать хороший GUI без богатого ООП очень трудно, хотя и возможно.
Вот как раз тот ООП, который есть в Go для написания GUI подходит куда больше, чем «классический».
Но GUI в Chrome в основном на JavaScript всё равно, так что тут скорее проблема в специфике продукта, а не в том, что GUI на Go сложно писать…TheShock
11.12.2017 04:27в Go для написания GUI подходит куда больше
Когда в руках молоток, все вокруг — гвоздиkhim
11.12.2017 04:42Причём тут молоток? При создании GUI вам регулярно нужно связывать между собой разнородные обьекты.
Что в модели ООП пропогандируемой С++/C#/Java сделать не так-то просто: недаром все C++ библиотеки так или иначе завязаны на кодогенерацию. Явную (как в MFC или Qt) или неявную (как в WTL — через шаблоны). В CLOS этой проблемы нет и, собственно, своременный GUI разрабатывался на этой обьектной модели ещё до того, как его увидел Jobs.mayorovp
11.12.2017 10:36Не совсем понимаю что такое «связывать между собой разнородные объекты». Не могли бы вы пояснить какие именно связи имеются в виду?
khim
11.12.2017 16:39Вот те самые, которые в Qt делаются через кодогенерацию, а в MFC и других подобных тулкитах — через ещё более сташные извращения.
ООП в стиле CLOS, принятый в Go решает эту проблему абсолютно естественным способом: так как интерфейсы и их реализация напрямую не связаны с типами, которые их реализуют, то страшные трюки в стиле WTL (когда у вас обьект-предок приходится делать шаблонным, чтобы он мог как-то обратиться к потомку) не нужны.
Впрочем, как я уже сказал, полноценных библиотек виджетов, сравнимых с Qt для Go пока нет, так что сказать на 100%, что всё пройдёт гладко и удастся обойтись без костылей типа кодогенерации (а макросы это тоже кодогенерация, пусть и примитивная) — пока нельзя.mayorovp
11.12.2017 21:21+1В таком случае я не понимаю претензий к C#, где то же самое является частью языка (аналог сингала — это событие, слотом может выступать любой метод подходящей сигнатуры) безо всякой кодогенерации.
khim
11.12.2017 21:52+1Замечание принято. Да, пожалуй в C# этой проблемы нет. Там среди прочего всякого добра напихано и решение этой проблемы тоже.
daiver19
11.12.2017 04:30Так вот, примерно 90% задач гугла вполне решается на этом самом «С для дураков» без использования кодогенерации, рефлексии, шаблонов, ООП и чего либо еще, и самое главное, что в итоге написанный разработчиками любого уровня код на Go смогут понять любые другие разработчики, даже если они в компании работают сутки и пришли сразу после ВУЗа.
Интересное умозаключение. А на самом деле большинству приходится делать всякие сервисы, высокопроизводительные серверы, машинное обучение итд. В итоге Go стал по большей мере более производительной альтернативой Python для написания несложных демок/скриптообразного кода.
mersinvald
11.12.2017 10:42+1Простота поддержки
Спагетти-кода, который го провоцирует?
простота отладки
В копипасте?
поиска ошибок
Особенно когда бажный код копипастой размазан по всей кодовой базе вместо использования дженериков, ага.
Source
11.12.2017 22:35минимальные шансы выстрелить себе в ногу
Ну да, как же… Gotchas and common mistakes in Go
По количеству WAT Go может влёгкую побороться за второе место после JavaScript.khim
11.12.2017 22:54По количеству WAT Go может влёгкую побороться за второе место после JavaScript.
Только за третье и то не факт. Первое-второе места забиты намертво за JavaScript'ом и PHP, которые всё никак не выяснят какой из них кривее.batyrmastyr
12.12.2017 09:09+1Хотите сказать кто вообще способен хотя бы догнать С++ и PL/1? Что-то во времена студенческие я не видел ни одного учебника по С++ (в особенности от авторов языка) в котором хотя бы 4/5 примеров компилировались, а с тех пор в язык только добавляют и добавляют новые способы стрельнуть себе в ногу.
khim
12.12.2017 11:00-3Что-то во времена студенческие я не видел ни одного учебника по С++ (в особенности от авторов языка) в котором хотя бы 4/5 примеров компилировались
Если что-то не скомпилировалось — так это ж хорошо: вы увидите проблему и её исправите. Это вообще не проблема.
А вот если оно скомпилировалось и делает не то, что ожидалось… это бяда. В этом отношении JavaScript и PHP не переплюнуть… хотя C++ и старается, да.mayorovp
12.12.2017 11:01+2Пример-то я исправлю, но я не могу исправить учебник. А кто-то по нему учится…
VolCh
12.12.2017 14:38Если что-то не скомпилировалось — так это ж хорошо: вы увидите проблему и её исправите. Это вообще не проблема.
Ну как сказать. Для человека, который свой первый язык учит по этой книге, которая для него основной источник информации и об языке, и о программировании вообще, это может быть большой проблемой.
khim
12.12.2017 16:52Для человека, который свой первый язык учит по этой книге, которая для него основной источник информации и об языке, и о программировании вообще, это может быть большой проблемой.
Для книги и обучения — да, это проблема. Для языка — нет. И показателем качества языка это не является ни разу (показателем качества книги — таки да).VolCh
12.12.2017 16:57Если в книге 3/4 примеров не компилируются или крашатся не из-за ошибок автора-издателя, а из-за того, что раз в год в языке происходят сильные изменения, ломающие обртаную совместимость, то к качеству языка это имеет прямое отношение.
khim
12.12.2017 18:25+1Несомненно. Но с учётом того, что у меня есть знакомый, который отличненько осваивал курс, написанный под Turbo C 2.0 с использованием последней версии GCC и Clion… я думаю что проблема-таки в издателях… Ну или в горе-программистах, которые в учебных примерах используют всякие <conio.h>, которые большинством компиляторов никогда не поддерживались.
sumanai
12.12.2017 19:48+1В этом отношении JavaScript и PHP не переплюнуть… хотя C++ и старается, да.
Не знаю за плюсы, но указатели в чистом С- это такой реактивный многозарядный гранатомёт со сверхчувствительным спусковым механизмом, по сравнению с которым сравнение строк в этих скриптовых языках- полнейшая фигня.khim
13.12.2017 08:43Теоретически — да. А практически — в типичном web-магазинчике будут десятки миллионов строк кода на C/C++ (не только ядро операционной системы, но и всякие SQL-базы данных, акселераторы и прочее — это ведь всё на C/C++ написано), а сверху — тысяч сто строк на PHP или Ruby. При этом на каждую дыру в ядре будет пара SQL-уязвимостей в движке.
Так что да — указатели они, конечно, ужасны и опасны, но до уровня JavaScript/PHP — не дотягивают…sumanai
13.12.2017 16:09Вы серьёзно сравниваете вылизанный код ОС, интерпретатора и БД, используемый на миллионах инсталяций, с уникальным наколеночным кодом студента на подработке (утрирую)?
batyrmastyr
13.12.2017 11:13Это был толстый намёк, что даже авторы стандарта не знают его от и до и (sic!) раз даже простой пример набрать без ошибок не могут. А без полноценного понимания языка говорить о совместимости его функций (= отсутствии косяков в дизайне) как-то смешно.
В этом смысле php намного лучше — пространства для косяков меньше, дефекты дизайна постепенно исправляются, глупости перестают интерпретироваться (в отличие от С++).
creker
11.12.2017 01:05И кто теперь более читабельный? Я отдам свой голос D. Его код куда более читаемый, так как он более явно описывает действия.
Типичный пример типичной статьи. Какой читабельный? Go. На D написан типичный ворох специальных символов, который надо декодировать каждый раз, когда ты приходишь к куску кода, который первый раз видишь или позабыл. Не говоря о позорной обработке ошибок Правильный вопрос здесь — какой код короче и это действительно D. Почему-то авторы подобных статей все время это путают. Я это прекрасно знаю по C#. Можно очень интересные вещи понаписать, но потом сам же свой код приходится декодировать, потому что он излишне «выразителен».
У нас есть потрясающие системы контроля версий с отличным теггированием и поддержкой версий, которые создатели Go игнорируют и просто копируют исходные тексты.
Есть такая вещь как reproducible builds. Вендоринг зависимостей с этим совместим, а там уже можно уже выбирать — копировать или сабмодулем делать. Просто импорты со ссылками на репозитории в надежде, что во время билда все это успешно скачается и соберется — нет.
Из всей статьи только дженерики адекватная критика. Их недостает и interface{} нужно всеми силами как-то выпиливать из стандартной библиотеки. «культурный багаж Си» так вообще ересь. Инкапсуляция и абстракция достигается через интерфейсы. Еще большая ересь это критика процедурного стиля. Какая разница как он ощущается, если этот стиль программирования понятен и прост для чтения?
Такое ощущение, что автор статьи студент, которому лишь бы поиграться с языком, в котором побольше фич. Человек будто совсем не понимает задач компаний вроде Гугл.SirEdvin
11.12.2017 01:08По поводу билдов немного странный аргумент. Почему нельзя было сделать нормальную систему пакетов, как смог тот же Nim или Crystal? Версии по коммитам это просто ад.
creker
11.12.2017 01:16Потому что нормальная система пакетов обычно зависит от стороннего сервиса, на который надеяться себе же дороже. Не так давно уже была забавная история с node.js что ли, когда удалили безобидный пакет. Банально интернет может не работать и что, вся компания будет сидеть и ждать его возвращения? В конечном итоге приходит понимание, что наверное стоит поднять тот же самый сервер локально, чтобы он хотя бы кэшировал зависимости. Либо окончательное прозрение, что стоит все зависимости тащить с собой в репозиторий в отдельной папочке «vendor». Судя по всему, компании уровня Гугл и даже куда меньше именно к последнему и приходят. Конечно, не идеал и не удобно бывает, но вариантов других особо нет.
SirEdvin
11.12.2017 01:59А как эти две вещи связаны? Храните где хотите, но отвяжите пакеты от VCS и заставте людей делать нормальные пакеты с версиями. Вам же его скачать то все равно с внешнего сервиса нужно.
arteast
11.12.2017 09:22Видимо, гугл считает, что им удобнее вот так. У них, к примеру, весь C++ код хранится в едином репозитории и собирается целиком. Казалось бы, милое дело — выделить модули, каждый модуль разрабатывать независимо в своем репозитории (и секюрность, и размер репо и билда уменьшить, и команды друг другу мержи и билды ломать не будут), стабильные оттестированные релизы пакетировать и выкладывать на тот же внутренний сервер — красота, все по святцам! Но нет, гуглу оказалось объективно лучше держать все в одной куче и жить на "острие".
Есть такая поговорка "ты не гугл". То, что может казаться (и быть) хорошим и приятным для нас с вами, для компании такого размера может был плохим и вредным. В частности, таким вредным они считают версионирование пакетов. Что логично — нетрудно обеспечить совместимость версии, если у тебя собирается пакет, зависящий от еще пары. Когда у тебя собираются тысячи и десятки тысяч пакетов, зависящих друг от друга, то менеджмент совместимых версий превращается в ад.SirEdvin
11.12.2017 10:26Когда у вас так много пакетов, у вас все равно ад.
Лично мне кажется, это потому, что google в целом может положить большой болт и тестировать новый функционал на 10% клиентов. Далеко не все компании так могут, так как не монополисты все-таки :)
bogolt
11.12.2017 10:50размер билда? В плюсах же по идее ты не платишь за то что не используешь.
arteast
11.12.2017 14:32Я о суммарном размере билда. Если кто-то изменяет библиотеку в модульной архитектуре, то им надо только проверить, что она собирается; сборка, проверка вышестоящих библиотек — это задача тех, кто будет обновлять зависимости. Если кто-то изменяет библиотеку в гугле, то им надо собрать всё, что ее использует, и поправить всё сломавшееся. Гугл, НЯЗ, использует виртуальную ФЗ (как ClearCase MVFS, чтобы не убивать место на HDD и время программистов миллионами файлов из репо), распределенные билды и хитрые билд-системы, чтобы скорость билдов была адекватной, но билды все равно должны быть сделаны и артефакты все равно должны куда-то упасть.
khim
11.12.2017 16:48Если кто-то изменяет библиотеку в модульной архитектуре, то им надо только проверить, что она собирается
А работоспособность кто проверять будет? Пушкин?
сборка, проверка вышестоящих библиотек — это задача тех, кто будет обновлять зависимости.
Ага — вот только они, в большинстве случаев, ни разу не заинтересованы в переходе на новую версию если у них всё «и так работает». И в результате вам либо приходится поддерживать 10 версий библиотеки, либо ваша библиотека оказывается 10 раз форкнута и засунута в разные проекты в разных видах. Либо и то и другое одновременно.
Когда у вас между проектами — административная граница, то у вас, в общем-то, нет выбора. Но если её нет — общий репозиторий лучше. Собственно все большие проекты так всегда и собирались — и UNIX, и OS/360 и многие их последователи.
Только GNU/Linux, по политическим причинам, устроен не так. Ну так то — вынужденная мера, а не то, к чему стоит стремиться.
но билды все равно должны быть сделаны и артефакты все равно должны куда-то упасть.
А в случае с несколькими проектами и несколькими репозиториями это не так?arteast
11.12.2017 18:03А работоспособность кто проверять будет? Пушкин?
Подразумеваем, что работоспособность библиотеки проверяется. Работоспособность или даже собираемость других библиотек — нет.
Но если её нет — общий репозиторий лучше.
Я с этим спорю, что ли? Я говорю, что для "нас с вами" (use-case типичный опен-сорс проектик) удобнее релизы и версионируемость. Для гугла — удобнее монорепозиторий и жизнь на "голове". Go — язык гугла для гугла; логично, что они использовали для зависимостей в Go ту же философию. Поэтому и статья в целом ("мне Go не нравится, а язнаю Dумный программист => Go не для умных программистов"), и тот комментарий, на который я отвечал ("для меня пакеты удобнее => Go нужны зависимости в пакетах, а не голова репозитория"), мне кажутся наивными. Я попытался намекнуть, что у гугла может быть точка зрения, отличная от ТЗ типичного Васи Пупкина, но это не оценили.
А в случае с несколькими проектами и несколькими репозиториями это не так?
Не так, по той причине, что не надо проверять собираемость вышестоящих библиотек. Грубо говоря, в "модульном" варианте выпуск новой версии Abseil влечет за собой билд библиотеки Abseil и прогон ее автотестов. В монорепозитории — (инкрементальный, но) ребилд всего репозитория из тысяч проектов, и прогон всех автотестов всех этих библиотек.
arvitaly
11.12.2017 09:48С версиями вот такие рассуждения.
У нас есть некий код, допустим он типизирован и есть внешний интерфейс у импортируемого модуля.
Что в таком случае будет означать изменение версии?
Изменение API модуля? Совсем не обязательно (в semver это предполагает лишь в мажорных версиях). А что тогда? Непонятно, все равно придется проверять, что же изменилось вручную.
Получается, что система версий не типизируется, а значит не автоматизируется.
А значит, весь вопрос уже заключается в том, как вести для людей лог изменений модуля, и я вовсе не уверен что безликие 3 циферки что-то значат.
И логика без цифр такая.
Если это внутреннее изменение (ну, фикс безопасности, к примеру), то и не нужны никакие новые цифры. Если изменение API, то, опять же, компилятор должен ругнуться. Если изменения API нет, но поведение изменилось — нужно менять название пакета, а старый оставить в покое, обратная совместимость, все дела.
Привязка же к коммитам удобна при параллельной разработке, но внутри одной компании.
А эти все куча цифр в некоторых пакетных менеджерах — попытка костылем подпереть отсутствие статической типизации. Все равно не работает.
SirEdvin
11.12.2017 10:24+1Версионность выполняет ровно две функции:
- Человекочитаемость. Когда я вижу, что пакет версии 2.1.5, а я использую версию 0.5.1, то мне надо обновится (и вполне вероятно, поиметь проблем). А когда я вижу, что использую коммит 249f069bdf09be30258c604be27fdce51694706f вместо 249f069bdf09be30258c604be27fdce51694706f это мне ни о чем не говорит.
- Исправность. Есть такая штука, что далеко не у всех людей каждый коммит содержит код, который вообще собирается. А каждая версия программы должна собираться, без этого никак.
Более того, версионность позволяет не только жестко фиксировать версии, но так же и задавать диапазоны версионности, что бы у вас не возникало по три-пять разных версий пакетов в одном и тот же проекте.
Ваши рассуждения возможно имели бы смысл, если бы не проблема, например, с внятными именами пакетов и тем, что настолько жесткая обратная совместимость никому не нужна, можно просто не обновляться, раз уж так нужно.
khim
11.12.2017 16:52Есть такая штука, что далеко не у всех людей каждый коммит содержит код, который вообще собирается.
И вот «с этой штукой» и нужно бороться. Trybots для этого и существуют.
Более того, версионность позволяет не только жестко фиксировать версии, но так же и задавать диапазоны версионности, что бы у вас не возникало по три-пять разных версий пакетов в одном и тот же проекте.
Откуда у вас возьмутся «три-пять» версий пакетов (и вообще пакеты, как таковые) при использовании одного репозитория? Или вы про master/release/release-stable и т.п. ветки? Ну так они «внутри себя» все согласованы…SirEdvin
11.12.2017 16:55Откуда у вас возьмутся «три-пять» версий пакетов (и вообще пакеты, как таковые) при использовании одного репозитория? Или вы про master/release/release-stable и т.п. ветки? Ну так они «внутри себя» все согласованы…
Давайте начнем с простого. Если вы работаете в google/facebook/another company где все проекты лежат в одном репозитории (на самом деле в google и facebook это уже тоже не так, у них есть open source) и вообще никак не завязаны на open source либы — я за вас рад.
А если же нет, то все очень просто. Вы скачиваете два разных проекта на go себе в репозиторий, и оказывается, что они используют разные версии пакетов, например, для логгирования. Это вполне реальная ситуация, как мне кажется. Вот у вас и две версии пакета для логгирования в рамках вашего проекта.
khim
14.12.2017 08:40-3на самом деле в google и facebook это уже тоже не так, у них есть open source
Угу — и это позволяет посмотреть как всё устроено.
Вы скачиваете два разных проекта на go себе в репозиторий, и оказывается, что они используют разные версии пакетов, например, для логгирования. Это вполне реальная ситуация, как мне кажется. Вот у вас и две версии пакета для логгирования в рамках вашего проекта.
Спасибо что показали наглядно почему зависимости в Go сделаны так, как они сделаны.
Когда вы скачиваете себе два разных проекта к себе в репозиторий, то они обязаны работать с той версией пакетов, что у вас есть. Можете посмотреть в Chrome или Android: пакеты там не дублируются (за исключением резко патологических случаях типа AngularJS vs Angular, или Pel5 vs Perl6 где разные «версии» лучше рассматривать не как разные версии одного и того же, а два совершенно разных продукта).
Вот чтобы такую проблему не решать Go по умолчанию и берёт всё из trunk'а. Если ваши пакеты, в результате, не заведутся и не заработают — ну да, можно попробовать накостылить что-нибудь. Но по умолчанию — все пакеты должны использовать одной версии. Самой последней.TheShock
14.12.2017 08:46+3То есть если в транке популярной библиотеки кто-то выложит новую версию, которая ломает совместимость, то по цепочке упадут все проекты, которые используют этот пакет, пока все их них не будут пофикшены?
arvitaly
14.12.2017 09:20Ну не путайте стадии. В production всегда должны фиксироваться именно те версии, на которых прошли все тесты. Любое обновление по любому поводу (даже patch) может происходить только на стадии разработки. Так что да, упадут все проекты, и встанет очевиден вопрос, с чего это используется библиотека, которая ломает обратную совместимость. Но нет, упадут не в боевой версии, а у разработчиков.
TheShock
14.12.2017 09:56+4Я разрабатываю какой-то продукт и использую множество библиотек. Делаю `
go get
` и скачиваю из транка новые версии библиотек. Оказывается, что в одной зависимости нескольких моих зависимостей сломалась совместимость. Теперь моя разработка стоит пока все эти библиотеки не пофиксят совместимость с той, что внесла изменения?
Обращу внимание, что gitflow не отрицает изменений, ломающих совместимость. То есть в мастер вполне может прилететь такая версия.khim
14.12.2017 12:53-2Я разрабатываю какой-то продукт и использую множество библиотек.
И вот в этот момент — у вас уже проблемы: если вы не понимаете что вы используете и допускаете использование бог-знает-чего, то кто ж вам судья, что у вас что-то сломалось?
Обращу внимание, что gitflow не отрицает изменений, ломающих совместимость. То есть в мастер вполне может прилететь такая версия.
Ничего не знаю про gitflow. Знаю что в gerrit есть кнопочка rollback, которая откатывает изменения без вытаскивания их в клиент и прочего. Вот именно на случай, когда в trunk попало что-то, что сломает кучу клиентов.
vintage
11.12.2017 11:16Как раз недавно рассказывал, почему мы отказались от версионирования на фронте.
unsafePtr у шарпа и явы дженерики — это не шаблоны, а полиморфные типы.
khim в C++ шаблоны сбоку прилепили, а потом внезапно обнаружили, что на них можно программировать. D уже проектировался с нормальной поддержкой шаблонов: сообщения об ошибках понятные, стектрейсы ведут в код шаблона, а не в генеренный код и тд.
JekaMas
11.12.2017 01:35Потому что создателям go "далеко за ..." и многое им видится "ненужным, излишним", после проходят годики и начинаются писаться статьи вроде Dave Chany и техническом долге go и том, что если язык назвался mainstream, то от него ждут определенных вещей: управление зависимостями, generics. Но создатели языка действуют в формате "если мы сомневаемся, то не делаем ничего и ждем".
Язык golang великолепен в большом поле задач и за год вполне можно взгрустнуть об отсутствии обобщенных типов лишь раз или два(грусть чаще накатывает от фанатов golang). Но вот станет ли язык mainstream — вопрос открытый.
По мне, несмотря на все громкие заявления о том, как будут вводиться изменения в язык (а таких заявлений была масса в 2017 году), развитие языка в 2017 провалилось: невнятный и непродуманный контекст, поломанные плагины (эпичная "бага" с тем, что они не работают нигде, кроме unix), алиасы, sync.map — это скорее недоразумения, чем развитие языка.
Но время покажет.
Rasifiel
11.12.2017 05:56Потому что язык был сделан для Гугла, где весь код живет в одной репе и собирается с головы/какой-то версии коммита.
Idot
11.12.2017 08:20На D написан типичный ворох специальных символов, который надо декодировать каждый раз, когда ты приходишь к куску кода, который первый раз видишь или позабыл
А можно, пожалуйста, подробнее для тех кто не в курсе?
acmnu
11.12.2017 12:03На D написан типичный ворох специальных символов, который надо декодировать каждый раз, когда ты приходишь к куску кода, который первый раз видишь или позабыл.
Я не пишу на D, но пример там очень понятный, поскольку похож на любой современный язык, кроме Go
TexxTyRe
11.12.2017 01:23Не понял: в статье ругают Go или рекламируют D? Объясните, я просто dumbed down (из Google).
mrobespierre
11.12.2017 03:11ругают Go или рекламируют D?
точно так. только по сниппетам кода понятно, что автор не дотягивает даже до туповатых, а умным себя считает только по факту того, что пишет на D (оказался умнее всех тех, кто не пишет на этом прекрасном языке)mrobespierre
12.12.2017 17:04[offtop]
минусующие (как здесь, так и в карме) могли бы и высказать в чём я не прав, аргументами меня задавить, а не так — плюнул в спину и убежал
[/offtop]VolCh
12.12.2017 17:05+4Вероятно за "автор не дотягивает даже до туповатых" и вообще переход на личности.
mrobespierre
13.12.2017 04:04[offtop]
Спасибо, мил человек, никогда бы и в голову не пришло, что на Хабре есть целый профсоюз «Защиты британских троллей от оскорблений за откровенно тупые набросы с интами». Буду знать.
[/offtop]
m68k
11.12.2017 01:25Как можно было, например, написать утилиту:
Заголовок спойлераpackage main import ( "fmt" "io/ioutil" "os" ) func main() { var err error reader := os.Stdin if len(os.Args) > 1 { if reader, err = os.Open(os.Args[1]); err != nil { panic(err) } } text, err := ioutil.ReadAll(reader) if err != nil { panic(err) } fmt.Println(string(text)) }
JekaMas
11.12.2017 01:42Вам доводилось пользоваться кодогенерацией в своих проектах? Довольно муторное, ненадежное и небыстрое занятие. По мне, это сваливание ответственности с компилятора на пользователей, особенно в части гарантий работы.
Стоит в проекте появиться 5-10 активно используемым кодогенераторам и появляется чувство, что он обложен черными ящиками, в которых могут быть ошибки, подчас трудно отлавливаемые — разбирать сгенеренный код бывает нелегко. Вместо одного гарантированного решения задачи с обобщенными типами получается зоопарк из разных пользовательских решений, которые надо поддерживать. Это банально дорого.
Кодогенерация, на мой взгляд, это не решение, скорее затычка "вы так хотели generics — нате!"khim
11.12.2017 02:06Стоит в проекте появиться 5-10 активно используемым кодогенераторам и появляется чувство, что он обложен черными ящиками, в которых могут быть ошибки, подчас трудно отлавливаемые — разбирать сгенеренный код бывает нелегко.
А кто вас заставляет кодогенерировать код, который сложно разбирать?
И 5-10 кодогенераторов — это 5-10 скриптов, которые что-то генерируют или 5-10 типов кодогенераторов (ну там ragel, bison, flex и т.д. и т.п.)? 5-10 типов (каждый со своим языком и описанием) — это уже перебор… собственно Go потому и появился чтобы всякие бесконечные Sawzallы не генерить в бешенных количествах…SirEdvin
11.12.2017 02:25Ну вот частый кейс для кодогенерации — это обход отсутствия дженериков. Как результат сделать читаемым, если это будут стены шаблонного кода?
Falstaff
11.12.2017 02:34Не в защиту (и не в поддержку тоже), просто хотел, размышляя вслух, сказать — это философский вопрос, надо ли вообще читать иной сгенерированный код. Ведь, взять например шаблоны в C++ — они фактически тоже генерируют код, просто на лету и он нигде не сохраняется. И программист работает с ними — приходится держать в голове, как будут инстанцироваться шаблоны.
SirEdvin
11.12.2017 02:42Но вы работаете с ними как с частью языка. Про это знают тесты, ide, система проверки кода, компилятор и прочее.А ещё оно неплохо интегрировано в язык. Со сторонней кодогенерацией вроде не так.
Falstaff
11.12.2017 03:04Зависит от кодогенерации (мы ведь просто о дженериках говорим). Я не настолько хорошо знаю Go, но вроде там есть какой-то инструментарий кодогенерации прямо из коробки. Понятно что, чем сложнее генерация, тем сложнее поддерживать, но вопрос инструментария — это уже частности, я просто говорил, что под капотом разницы нет, в C++ тоже кодогенерация, и программист сгенерированный код не читает, ему приходится пользоваться головой и инструментами, какие есть. Причём и там не всё радужно, шаблонный код часто совсем неподдерживаемый а сообщения об ошибках в шаблонном коде — нечитаемая портянка на весь экран. :)
l4l Автор
11.12.2017 03:10в C++ тоже кодогенерация, и программист сгенерированный код не читает, ему приходится пользоваться головой и инструментами, какие есть. Причём и там не всё радужно, шаблонный код часто read-only а сообщения об ошибках в шаблонном коде — нечитаемая портянка на весь экран.
Каша из логов шаблонного кода со временем превращается в понятное (пусть и все еще большое) чтиво, сам в это когда-то не верил. Ну и вообще говоря можно получить код с инстанциированными шаблонами (e.g
clang -Xclang -ast-print -fsyntax-only ...
), вдобавок отладчики уже давно выводят типыFalstaff
11.12.2017 03:18Ну, весь вопрос — сколько времени понадобится, чтобы превратилась в понятное чтиво. :) По сути это тоже перенос нагрузки на мозг разработчика. Поддержка тоже местами хромает — в CLion у меня, например, целая куча красного кода из-за С++14, если говорить об IDE.
Это я всё к тому, что, когда кричат "кодогенерация — плохо, шаблоны — хорошо", обычно забывают, что шаблоны — это та же генерация, только внесённая в стандарт языка и (в лучшем случае) обложенная тулингом.
0xd34df00d
12.12.2017 00:03Каша из логов шаблонного кода со временем превращается в понятное (пусть и все еще большое) чтиво, сам в это когда-то не верил.
Я в это тоже когда-то не верил, потом стал верить, недавно опять перестал. В итоге приходится методом научного тыка выводить минимальный воспроизводимый пример и вдмучиво на него смотреть.
вдобавок отладчики уже давно выводят типы
Это если у вас код таки собрался.
alexeykuzmin0
12.12.2017 18:08сообщения об ошибках в шаблонном коде — нечитаемая портянка на весь экран
С появлением концептов это должно уйти в прошлое.
0xd34df00d
11.12.2017 23:58Ведь, взять например шаблоны в C++ — они фактически тоже генерируют код, просто на лету и он нигде не сохраняется.
А очень жаль. В нетривиальных случаях хотелось бы, чтобы можно было как-то адекватно это дело подебажить.Falstaff
12.12.2017 15:34Выше l4l запостил один способ посмотреть код с инстанцированными шаблонами — правда, только для clang. Но в целом да, шаблоны — это часто упражнение на кодогенерацию в уме. :)
0xd34df00d
12.12.2017 19:33Ну я всё равно пользуюсь только clang, так что ничего страшного. Но вообще это как полноценный дебаггер и анализ дизасма при отладке рантайм-проблем — дампать инстанциированное представление ближе к дизасму, а всё-таки хотелось бы что-то ближе к дебаггерам.
JekaMas
11.12.2017 02:56Неправда ли любопытно: кодогенерация — это рекомендуемый путь, но стоит им пойти и начинаются проблемы.
Так в прошлом проекте использовались генераторы: msgpack для aerospike, easyjson, для клонирования объектов, локальный типизированный кэш, генератор клиента на основе api. Некоторые проекты использовали большее число.
Иногда в сгенерированном коде находились ошибки и тут приходится читать этот код. Но кодогенераторы пишут кто во что горазд (в общем-то и понятно — это просто сторонние пакеты, стандарта на них нет) и разбираться в таком хозяйстве долго и дорого.
Для меня, кодогенерация — это перекладывание ответственности и гарантий с больной головы на здоровую.khim
11.12.2017 04:07Для меня, кодогенерация — это перекладывание ответственности и гарантий с больной головы на здоровую.
А какая альтернатива? Шаблоны? Ну так это то же самое, только ещё хуже. Потому что там всё точно также генерируется — только неявно. Причём в случае с кодогенерацией вы можете увидеть и понять — что происходит просто открыв сгенерированный код. А если при открытии сгенерированного файла у вас IDE сжирает несколько гиг памяти, то сразу возникает вопрос: а оно точно нам нада — то при использовании шаблонов можно заметить что «что-то пошло не так», только обнаружив, что линкеру 16GB памяти не хватает и нужно ставить 32GB только чтобы получить один бинарник на гигабайт…
Даже на C++ я часто вижу, что люди используют кодогеренрацию, а не шаблоны. Потому что как средство метапрограммирования кодогенерация зачастую — удобнее.0xd34df00d
12.12.2017 00:08Дженерики как в хаскеле. Это не шаблоны в смысле плюсов и не template haskell в смысле полноценной компил-тайм-манипуляции AST, а просто реализация наблюдения, что типы данных обычно изоморфны рекурсивным кортежам. Через это можно и хеши делать, и операции сравнения, и вывод на экран, и жсон, и БД, и вообще большую часть того, что надо от кода, претендующего на манипуляции с AST и всяким таким.
А, ну а вообще полноценной системы типов хватает, когда вы можете написать функцию, которая в своей сигнатуре говорит, что она работает с любым типом, реализующим Ord, например, или вроде того. Это вообще 99% всех использований покрывает.
Zanak
11.12.2017 12:58+1Кто же сказал, что go процедурный язык? В чем проблема обеспечить инкапсуляцию? Хоть интерфейсом, хоть просто выносом в другой пакет
ООП не исчерпывается инкапсуляцией, есть еще абстрагирование, наследование и полиморфизм. С наследованием Go еще хоть как то справляется, а с 2 оставшимися, на мой взгляд — совсем печалька.
mayorovp
11.12.2017 12:59Более того, пример javascript убедительно показывает, что именно инкапсуляция-то в ООП и вовсе оказалась необязательна :-)
Zanak
12.12.2017 13:52JavaScript — не совсем удачный пример. JS сейчас — это нехилая инфраструктура, со своими препроцессорами, упаковщиками, и кучей чего еще, чтобы разрабам было приятно, и обходится без чего они, даже если смогут, вряд ли захотят.
truebest
11.12.2017 01:25Самым главным в программировании я считаю это умение решать задачи. Для решения задачи есть инструменты, и язык это инструмент, в частности, go тот язык, которым быстро можно овладеть. С моей точки зрения нужно использовать инструмент, который быстрее приведёт вас к решению задачи. Кроме того есть ряд известных преимуществ языка GO, например производительность, в отличие от JAVA.
Предположим что вы решаете задачу, ту, которую до вас не решал никто, на каком языке она будет решена — неважно, важно что решена, именно это и определяет потенциал человека. Простой пример, это гений Перельман, который блестяще решил теорему Пуанкаре. В вашем же случае, умные программисты — это программисты с завышенным ЧСВ. Или как это сейчас модно, знающие лайфхаки, в языках, из разряда, как сделать чтобы коврик в туалете не уезжал из под ног. Именно этот набор знаний, вы и выдаёте за ум.
Более 8-ми лет я пишу прошивки под разные микроконтроллеры, и конечно же на си, который я знаю также, как истинные верующие отченаш. Когда нужно организовать простенький сервер, для приема и обработки данных с железки, я часто пользуюсь Go. И здесь вопроси умения возникает мало. Другое дело, когда нужно помочь с реализацией на Java, или Android. Из-за ООП, мне гораздо сложнее, тк мне необходимо продумать по сути архитектуру и структуру классов. Это даётся мне сложнее, но только по тому что это не основной мой навык.
third112
11.12.2017 02:40конечно же на си
А почему не на ассемблере? Значит для Вас важен язык. Это, на мой взгляд, противоречит Вашим словам:на каком языке она будет решена — неважно
truebest
11.12.2017 11:12Можно и на ассемблере, только у каждой архитектуры свой набор команд. А это время. Если в задача — максимально короткое и эффективное время исполнения (лучше чем скомпилировал бы компилятор си), я использую ассемблер. Сейчас любой компилятор позволяет комбинировать си и асм, опять же для удобства и быстроты реализации задачи.
Время это очень ценный ресурс.
Поэтому для меня важно умение решать сложные задачи за максимально короткое время. Поэтому в Go я гораздо быстрее разберусь чем в JAVA
bevice
12.12.2017 12:30На С потому что для микроконтроллеров это без вариантов, если вы не хотите заниматься извращениями и нужна хорошая производительность. Не плюсы, потому что ABI нужно дописывать, он платформозависим. Asm для критически-важных (по скорости) секций, хоть это и значит, что запас минимален и кристалл выбран неправильно.
Да, можно сейчас на LUA писать «прошивки», но когда нужна прямая работа с памятью (записать такой-то бит в ячейку с таким-то адресом), или когда на практике знаешь, зачем нужны volatile-переменные, С просто удобнее.
third112
11.12.2017 01:44Еще одна скрипучая проблема в том, что Go — это процедурный язык (подобно тихому ужасу Си). В итоге начинаешь писать код в процедурном стиле, который чувствуется архаичным и устаревшим. Я знаю, что объектно-ориентированное программирование — это не серебряная пуля, но это было бы здорово иметь возможность абстрагировать детали в сами типы и обеспечить инкапсуляцию.
Интересно, что и в упомянутой книге (перевод) слово «объект» встречается три раза, а слово «класс» — только два: первый раз не по делу, а второй раз:
В Go есть несколько различных типов для представления чисел. Вообще, мы
Но если верить Википедии, Go — ОО язык (в отличае от Си):
разделим числа на два различных класса: целые числа и числа с плавающей
точкой. (С.15)
Специальное ключевое слово для объявления класса в Go отсутствует, но для любого именованного типа, включая структуры и базовые типы вроде int, можно определить методы, так что в смысле ООП все такие типы являются классами.
Из дальнейшего текста вики видно, что принципы ООП понимаются в этом языке довольно своеобразно.
lynch513
11.12.2017 03:21Вот взять например язык для «умных» и выразить вторую задачу:
putStrLn $ show $ foldl1 (+) [1, 2, 3.3]
И это будет работать на всех числах которые могут складываться.
Хорошо решим первую задачу:
import System.Environment import Data.Char main = getArgs >>= printFile where printFile (x:_) = readFile x >>= putStr printFile [] = getContents >>= readFile . filter (not . isControl) >>= putStr
Сколько надо затратить труда, чтобы найти это решение на Haskell? А если добавить обработку исключений? Наверно Go это другая сторона медали и вполне имеет право на существование.l4l Автор
11.12.2017 03:41Первый пример крайне просто читается, даже человеком со знанием английского (при условии, что он догадается, что
str
->string
иfoldl
->fold
).
Второе уже требует знания монад и ФП, тогда не должно являться большой проблемой.
А что касается времени на написание — то ФП, это, по моему опыту, совсем другие дебри. "Что-то накидать хоть немного работающее и сделать это быстро" — явно не про оное. Так что думаю сравнение не очень уместно. Впрочем, мой опыт может быть и не столь обширен
0xd34df00d
12.12.2017 00:21Ну, мой опыт с вашим не согласен. Как раз быстро накидать что-то немного работающее получается быстрее на нём.
0xd34df00d
12.12.2017 00:21Зачем
foldl1 (+)
, когда естьsum
, который уже давно работает поверх произвольного Foldable?
И я бы второй пример переструктурировал немножко:
main = getArgs >>= file >>= readFile >>= putStr where file (x:_) = pure x file [] = filter (not . isControl) <$> getContents
lynch513
12.12.2017 19:37Да вариант действительно отличный. Только сколько людей могут предложить такой вариант? Сколько людей знают что такое аппликативный функтор и что монада тоже им является? А в статье написано сел и за один вечер освоил, быстро накидал и в продакшн (((
main = do args <- getArgs stdIn <- getContents let file = if null args then filter (not . isControl) stdIn else head args content <- readFile file putStr content
Я использовал do нотацию как мог, чтобы упростить. Правда похоже на императивный код? Я его за минуту написал, а над прошлым вариантом задумался, хотя он много лучше0xd34df00d
12.12.2017 19:50Только сколько людей могут предложить такой вариант?
Всё дело в эстетике :) У вас вот putStr в обоих ветвях повторяется, поэтому естественный вопрос: а можно ли его как-то оттуда вынести и обобщить, например? Вынесли, теперь readFile повторяется. И так далее.
Сколько людей знают что такое аппликативный функтор и что монада тоже им является?
Любой, кто знает, что такое монада (когда я только изучал хаскель во времена ghc 6 и задолго до applicative/monad proposal, равно как и задолго до моего упарывания теоркатом, помню, сильно удивлялся, почему Monad не является субклассом Applicative). Но да, я согласен, таких людей меньше, чем программистов на Go.
А в статье написано сел и за один вечер освоил, быстро накидал и в продакшн (((
Ну, я бы не хотел работать в компании, где в продакшен выкатывают продукты освоения за один вечер. Собственно, никаких вопросов и претензий — у Go своя ниша, описанная что в этой статье, что кучу раз до неё.
Terras
11.12.2017 06:04Go изначально разрабатывался для того, чтобы переписать тормознутые сервисы на python/php/perl, и не отстрелить себе ноги на (C/C++). Что этот язык и сделал. То, что из него стали делать вундервафлю под все задачи (как это в свое время делали из node.js) — просто хайп.
Что касается сравнения GO с другими языками, в частности Java, то Google разработала Go и начала его активно использовать, чтобы отказаться от зависимости от Oracle. Заметьте одну вещь.
- Oracle — Java
- Microsoft — C#
- Google — Go
- Facebook — php (php Hack)
- Apple — Object-C/Swift
Просто крупные игроки не хотят зависеть от какой-то третьей стороны, поэтому делают свой «улучшенный язык», который и продвигают везде.
Сейчас Google замкнет всю свою систему на GO и станет таким же вендор локом, как и Microsoft, как Apple и прочее =)AngReload
11.12.2017 10:31+1Можно ли дополнить список Mozilla с её Rust? Кажется, новые языки создают все кто может.
JekaMas
11.12.2017 11:21У go не получилось в этом смысле — проект go-mobile скорее мертвый, чем живой, и сам Google для мобильной разработки советует Kotlin.
ffriend
11.12.2017 17:35Просто крупные игроки не хотят зависеть от какой-то третьей стороны, поэтому делают свой «улучшенный язык», который и продвигают везде.
Да нет, не думаю — для Android, например, Google не стал изобретать новый язык программирования, а просто сделал свою реализацию рантайма.
К тому же, потребность в своём собственном языке никак не влияет на дизайн этого языка. Задачи Google не так сильно отличаются от задач тысяч других компаний — (микро)сервисы с максимальной утилизацией CPU и памяти. Это объясняет горутины, например. Стремление к простоте изучения и близости к C тоже понятна, учитывая тысячи работающих у них студентов, хотя для многих других компаний это свойство языка может быть совершенно ненужным или неуместным. А вот замена дженериков на кодогенерацию или использование зависимостей без чёткого версионирования лично для меня ну совсем непонятны.
Bookvarenko
11.12.2017 07:17Отлично! Как-раз искал что-то вроде С для дураков. Ещё и с книжкой сразу. Спасибо!
mrobespierre
11.12.2017 07:47+1Если нужна книжка по Go — смотрите в сторону Кернигана (а вот его сишную лучше не читать).
Bookvarenko
11.12.2017 12:43Спасибо! А библиотеку для обмазывания цветом и рамочками вывода в консоль какую лучше использовать?
mrobespierre
11.12.2017 16:24Если я вас правильно понял, то вы хотите ncurses для Go. Так вот, его нет. Попытки были, но когда я пробовал — всё было не оч. Если сильно хочется некомандный интерфейс, то стоит смотреть в сторону http. Серьёзно, 5 минут делов, всё необходимое уже есть в стандартной библиотеке, нет проблем с переносимостью. Если не устраивает — стоит спросить другие варианты у коллективного разума в русском Go Slack сообществе.
vintage
11.12.2017 10:05void main(string[] args) { try { auto source = args.length > 1 ? File(args[1], "r") : stdin; auto text = source.byLine.join.to!(string); writeln(text); } catch (Exception ex) { writeln(ex.msg); } }
Тут не нужно перехватывать исключения, ибо их поведение по умолчанию и так эквивалентно тому, что написано в го версии. Так что правильнее так:
void main(string[] args) { auto source = args.length > 1 ? File(args[1], "r") : stdin; source.byLine.writeln; }
void main(string[] args) { [1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln; }
Этот код можно ещё упростить:
void main(string[] args) { [1, 2, 3, 4, 5].reduce!q{ a + b }.writeln; }
Не говоря уж о том, что в стандартной библиотеке уже есть нужный алгоритм работающий с любыми числовыми типами:
void main(string[] args) { [1, 2, 3, 4, 5].sum.writeln; }
UA3MQJ
11.12.2017 10:13В нем нет новых идей, кроме поддержки параллелизма (который, кстати, прекрасен) и это обидно.
Не так уж и прекрасен. Смотря с чем сравнивать )linuxover
11.12.2017 11:50а с чем?
если говорить об утилизации одного CPU и читабельности кода, то все рано или поздно приходят к тому что калбек-лапша это хелл, приходят к переключению контекста руками — yield/cede, корутины.
если далее говорить о межпроцессном взаимодействии, то все рано или поздно приходят к тому что mutex'ы, семафоры — это хелл, приходят к каналам и событиям.
если говорить о соединении двух подходов — получаем примерно то же самое: сопрограммы/корутины (чтоб читабельно) + каналы (чтоб межпроцессно). Что Go из коробки и предоставляет.
да у Go море недостатков, но Вы говорите — есть лучшие альтернативы.
лучшие это какие?
python, perl, прочие — это все языки одного CPU. прекрасные, но одного CPU
Erlang — язык прекрасный, но порог вхождения ужасен. Как объяснить кому-то что понятия переменная нет и это хорошо — хз.
Хацкель — это уже совсем фанатам.
что еще?
UA3MQJ
11.12.2017 12:59+1А если не переключить контекст руками — он так и будет принадлежать горутине, пока она его не отдаст (не вызовет yield либо внешнюю функцию). Есть возможность зациклить горутину и потерять один тред насовсем.
Не планируется ли введение в рантайм каких то механизмов для идентификации процессов горутин с возможностью их принудительного завершения работы?
По поводу каналов — да, это типичное решение для обмена. Но мне ближе обмен сообщениями между процессами с почтовыми ящиками (заумно — модель акторов). Я не критикую каналы, просто CSP и Акторы — это разные вещи, со своими особенностями. Мне в каналах не нравится то, что канал — это дополнительная сущность; факт того, что отправляющий процесс блокируется, если на той стороне канала не читают, умерла горутина.
Да, я в курсе, что в Go это стараются решить. И я буду рад узнать о том, что решили.
Альтернативы всегда есть, но всегда можно найти причины, по которым эти альтернативы вам не подойдут.linuxover
11.12.2017 13:26А если не переключить контекст руками — он так и будет принадлежать горутине, пока она его не отдаст (не вызовет yield либо внешнюю функцию). Есть возможность зациклить горутину и потерять один тред насовсем.
и это же хорошо. Ну а безо всяких go пользователь на любом языке может написать
while true; do nothing(); done
и отстрелить тред/процесс насовсем. Но мы же обсуждаем языки, а не тех кто этими языками себе что-то отстреливает?
По поводу каналов — да, это типичное решение для обмена. Но мне ближе обмен сообщениями между процессами с почтовыми ящиками (заумно — модель акторов).
не очень я понял при чем тут акторы. я говорил о транспорте данных между процессами. при обмене данными между процессами: можно класть их в shared memory около которой mutex. Можно гнать данные через канал/пайп/сокет. Больше способов не придумали пока.
А как оно оформлено поверх транспорта — актор там или не актор — дело десятое
Мне в каналах не нравится то, что канал — это дополнительная сущность; факт того, что отправляющий процесс блокируется, если на той стороне канала не читают, умерла горутина.
дык каналы с заданным размером решат эту проблему. Или даже резиновые каналы.
вопрос в том что если горутина умерла навсегда, то рано или поздно все равно придется решать эту проблему.
Да, я в курсе, что в Go это стараются решить. И я буду рад узнать о том, что решили.
Альтернативы всегда есть, но всегда можно найти причины, по которым эти альтернативы вам не подойдут.задача стоит IPC между процессами. Вариантов всего два: mutex (варианты) + shared memory. Здесь в полный рост проблема стоит взаимных блокировок, производительности, сетевого масштабирования. Второй вариант — пайпы (сокеты/каналы итп). Здесь та же проблема взаимных блокировок (что Вам не нравится) + буфферизации каналов (появляется понятие "данные в пути").
есть еще какие-то альтернативы?
UA3MQJ
11.12.2017 14:00вопрос в том что если горутина умерла навсегда, то рано или поздно все равно придется решать эту проблему.
Как?linuxover
11.12.2017 14:06- исправить код вызывающий ошибки (за разработчика это никто не сделает)
- перезапустить (написать перезапускатор), опять же за разработчика никто это не сделает
Можно вынести перезапускатор — на уровень языка или стандартной библиотеки. Вопрос только в формализации критериев.
JekaMas
11.12.2017 16:03Беда в другом: у меня есть несколько тяжелых обработок данных/внешних вызовов, которые выполняются в горутинах на каждый запрос; одна из горутин возвращает ошибку, как мне не транжирить ресурсы и прекратить выполнение тяжелых операций в других горутинах? Никак.
koluchiy01
13.12.2017 13:47На этот случай есть контекст, через который можно потушить дочерние горутины. Родительская горутина, которая всех запустила узнает об ошибке и через контекст тушит остальных
JekaMas
13.12.2017 13:50Оно так не работает)
Попробуйте набросать пример — будет любопытно посмотреть.
JekaMas
13.12.2017 14:35Попробовал набросать пример на основе документации на context — play.golang.org/p/7BHDtAgVMn
Попробуйте запустить на локальной машине и посмотреть итог (в песочнице должен быть deadlock поскольку там только один thread и он будет сразу заблокирован).
Context.Cancel может оповестить горутины о том, что пора `Done()`, но он не может прервать их выполнения. Такого механизма в go нет.
Из этого есть еще одно следствие — Golang неполноценно реализует CSP модель Хоара, поскольку в его модели над процессами для полноты модели определялся рад операций, в том числе и прекращение процесса. А golang этого не умеет и модель CSP реализует лишь частично.
GeckoGreen
13.12.2017 14:39Горутины являются такими легковесными благодаря тому что известны места, где может произойти смена контекста. Естественно, что прервать её выполнение в любом месте не выйдет. Можно использовать контекст и периодически проверять не отменили ли задачу.
JekaMas
13.12.2017 14:56Не совсем верно, до 1.5 вполне была возможность прерывать выполнение горутины. Да, через unsafe, но была, после ее убрали.
Время от времени проверять можно, если операцию можно разбить на отдельные части, между которыми делать проверки. Но как итог мы усложняем код на порядок.
Как правило остается только смириться с тем, что механизма для early stop и сохранения ресурсов нет и не будет.khim
13.12.2017 16:06+1Как правило остается только смириться с тем, что механизма для early stop и сохранения ресурсов нет и не будет.
И слава богу. В C++ мире от pthread_cancel тоже, в общем, отказались.
Не окупает оно себя. Теоретическая красота модели не компенсирует сложности, требуемые для того, чтобы её реализовать — и на практике оказывается, что пользоваться этим всё равно нельзя, так как разработчики разных библиотек не разрабатывают их так, чтобы их можно было безболезненно останавливать.
UA3MQJ
11.12.2017 14:06не очень я понял при чем тут акторы. я говорил о транспорте данных между процессами.
arild.github.io/csp-presentation
Слайды 10, 11, 12.linuxover
11.12.2017 14:10на слайде 10 справа нарисован аналог канала, ага.
на транспортном уровне все сводится к двум вариантам:
- shared memory + mutex
- pipe
а "актор" или "канал" или "lock" — это надстройки над тем или иным транспортом
0xd34df00d
12.12.2017 00:28есть еще какие-то альтернативы?
Distributed STM. Она все эти пайпы-шмемы прячет под капотом.
RPG18
11.12.2017 14:20+1Как показывает опыт Elrang, с акторами то же бывают проблемы.
факт того, что отправляющий процесс блокируется
а в том же Eglang получаем всякие утечки и раздувание очередей, если кто-то много пишет, а другой не успевает вычитывать. Что не бери, все равно получается, что жизнь штука сложная.
linuxover
11.12.2017 14:22> а в том же Eglang получаем всякие утечки и раздувание очередей, если кто-то много пишет, а другой не успевает вычитывать
дык резиновый канал никто не мешает и на Go написать, только это просто перенос проблемы из одного места в другое.RPG18
11.12.2017 14:29Проблема ли это? В данном случае, это решает часть проблем связанных с тем, что писатель работает быстрее читателя.
linuxover
11.12.2017 14:33> Проблема ли это?
конечно
> В данном случае, это решает часть проблем связанных с тем, что писатель работает быстрее читателя.
если писатель быстрее читателя, то данные накапливаются в памяти и в итоге переполняют ее.
мало того с какого-то момента пользователи начинают «видеть» лаг исполнения поставленных ими задач. То есть факап происходит даже еще ДО достижения мемори-лимита.
и никакой язык, а только разработчик может поправить эту проблемуRPG18
11.12.2017 14:41и никакой язык, а только разработчик может поправить эту проблему
Только это порой выглядит как костыль. Как например mutex в Erlang'е.
linuxover
11.12.2017 14:44Проблема ли это? В данном случае, это решает часть проблем связанных с тем, что писатель работает быстрее читателя.
смотря какое приложение конечно, в интерактивных мы вот пришли к такой парадигме:
- некто — источник событий — генерирует в пространство событие "у меня тут данные появились" данные прикладывает к событию
- события хранятся в некоем хранилище, хранение в котором сугубо временное.
- кому надо может подписаться на событие
от модели акторов отличается тем что
- в явном виде отсутствует адресат-получатель
- подписчиков может быть множество
- единый резиновый (но с fixed MAX size, fixed ttl) maildir для всех событий
подобный подход и решает проблемы с разными скоростями читателей/писателей и с переполнением памяти.
но проблемы читателя становятся проблемами читателя. Читатель может продетектить то что он продолбал часть данных и принять какие-то меры к исправлению ситуации (для интерактивных задач тут всегда можно что-то придумать, а вот для задач обработки данных типа конвертации видео — могут быть сложности)RPG18
11.12.2017 15:22И тут спрашивается причем тут теория взаимодействующих процессов с CSP, акторами и т.д.?
linuxover
11.12.2017 15:25не понял я вопроса.
есть проблема (1 штука)
есть несколько ее решений:
- заблокировать нафиг писателя пока читатель не прочитает (Go из коробки, Erlang — с телодвижениями) — набор проблем 1
- не блокировать (Erlang из коробки, Go — с телодвиженияи) — набор проблем 2
- промежуточный (Erlang с телодвижениями, Go — с телодвижениями) — набор проблем 3
кому какой вариант нужен — тот такой и выбирает.
а теория она при каждом варианте :DRPG18
11.12.2017 15:50В том то и дело. Если брать языки со встроенными моделями, то везде имеем костыли и велосипеды. Поэтому говорить, что акторы было бы лучше, чем CSP как-то неправильно.
gatoazul
11.12.2017 22:57Perl 6. Высокоуровневые вещи для параллельного программирования вставлены прямо в язык.
linuxover
12.12.2017 12:55я последние 15 лет программирую в основном только на Perl 5. Очень люблю этот язык.
смотрел на Perl 6 — крайне отрицательные ощущения: да в язык воткнули многопоточность и асинхронность, но зачем так сильно ломать обратную совместимость?
вот например регекспы: люди их пишут 100 лет одинаковые. Развивать? развивай!
но зачем скобки например тасовать причем по кругу? Добавили треугольные, ок, зачем менять смысл у других скобок? чем варианты старые не устраивали?
и так все. Perl 6 от Perl практически ничего не взял кроме какого-то ОЧЕНЬ ОБЩЕГО подхода. Поэтому хорошо бы ему не называться Perl и у него будет все хорошо.
0xd34df00d
12.12.2017 00:26Фанатам вот сейчас было обидно.
Конкуретность и параллельность отличная, от тупого
parMap rdeepseq
черезPar
-монаду к STM иasync
. А STM — божественная штука, вы просто пишете код, и он просто работает. Без дедлоков, без всего этого вот.
Daar
11.12.2017 10:38Использую Go последние лет 6 уже если не больше, и так привык, что практически перестал что-то писать на других языках, так немного на JavaScript еще для фронта пишу, и то уже WebAsm не за горами, думаю и его на Go можно будет компилить.
Очень сильно устраивает простота языка и еще главное независимость от кучи библиотек. Скомпилировал 1 файл, раскидал по серверам и полетело, не надо кучу зависимостей тащить и потом отлавливать конфликты или баги обновлений.danforth
13.12.2017 14:40Плюсанул бы, но не могу. Go для тех, кто устал от тонн абстракций, сотен мегабайт библиотек. Если нужно решить какую-то задачу, ты просто её решаешь, максимально простым и быстрым путем. Код очень простой и читабельный, понимание чужого кода приходит на много быстрее, чем в других языках. В этом его прелесть. А кому краткость кода, то выбирайте Perl, не ошибетесь.
Symphel
11.12.2017 10:51Каждый раз одни и те же тезисы в разных статьях о минусах Go, просто переформулированные.
> Не очень выразительный
Я не знаю, какие еще конструкции имел в виду автор, но в пример он привел исключения. В Go нет исключений, в FAQ есть пункт почему, нет и соответствующих кострукций. Возвращается ошибка, ее надо обработать. В C хотя бы были макросы, чтобы не копипастить, но в Go их нет. Кроме того, в Go у интерфейса error нет даже метода, который вернет код, только строковое представление. Так что если хочешь среагировать на конкретную ошибку — найди ее реализацтию и проверь type cast'ом. Или сравни текст и надейся, что не будет ложного срабатывания на другие ошибки с похожим текстом. Конечно, внутри приложения обычно все-таки используются типизированные ошибки, а не интерфейс error.
В сравнении с исключениями, где тебе достаточно знать тип исключения, на который ты хочешь среагировать, выглядит уныло. С другой стороны, ты не обязан знать, что конкретно может пойти не так, и ловить все ошибки по типу, а реагировать на фейл конкретного метода.
> Ад копирования
В Go нет типообезопасного обобщенного программирования. Когда будет — неизвестно. Копипаста, боль, страдания.
> Простой обход системы типов
Увы, если нет обобщенного программирования на этапе компиляции, оно будет на этапе исполнения. Ничего удивительного.
> Горе управления зависимостями
> нельзя указать версию
В стандартном инструменте go get нельзя указать версию, в менеджерах зависимостей glide и govendor — можно. Скоро обещают ввести стандартный менеджер зависимостей. Зависимости выкачиваются в поддиректорию /vendor для каждого пакета. Тут как раз все удобно.
> Культурный багаж из Си
> нет новых идей, кроме поддержки параллелизма
Непонятно, новых по сравнению с C или со всеми другими языками. Утиная типизация в компилируемом языке? Cтатически слинкованная стандартная библиотека и GC без зависимостей на libc размером в ~1МБ? Трансляциия в язык ассемблера целевой платформы, благодаря чему кросс-компиляция встроена в стандартную поставку? Отличий от С еще больше.
> Простота для собственной выгоды
Мы тут горшки обжигаем. После кода на Go, приятно сесть и написать, например, немного обощенного кода на C++. Но потом, возможно, его перепишут другие люди, и не раз. Которые тоже горшки обжигают, и у них дедлайны, и голова болит, и дома надо быть в 8. И мне придется его отлаживать.Bonart
11.12.2017 11:28В Go есть исключения в виде defer-panic-recover.
Но сахара для обработки ошибок кот наплакал и в результате на одну строку полезного кода приходится три шаблонного.
TimsTims
11.12.2017 11:11А разве в первом примерно когда идет считывание файла, не происходит ли там передача потока в другую goroutine? Сам go ещё не знаю, но насколько мне представляется схожесть с nodejs, во время считывания файла, и ожидания ответа от HDD, возможно фоновое выполнение другой подзадачи. И в таком случае, go сильно выигрывает у D и у C, т.к. передачу контекста там реализовывать сложнее чем в го.
snuk182
11.12.2017 11:37+1Нет. Пример абсолютно однопоточный.
Go с Javascript (и нодой) сравнивать по языковым конструкциям некорректно, поскольку в JS многопоточность не подразумевалась, и авторам пришлось совать в язык неявные async-костыли, чтобы не вешать нарисованный в том же потоке интерфейс. А у Go многопоточность самая обычная — ее нет, если явно в коде не попросить.
(Ну да, если придираться, то системных потоков там несколько, но у программиста к управлению ими доступа почти нет, и с точки зрения программы логический поток у нее один)JekaMas
11.12.2017 16:07Не совсем, ключевые вещи из стандартной библиотеки будут выполняться многопоточно и без вмешательства пользователя. Каждый запрос, приходящий в 'http/net' (HandleFunc, ListenAndServe).
VBKesha
11.12.2017 11:23Хм если появились статьи почему Go плох, значит уже стоит обратить на него внимание.
humbug
11.12.2017 11:30- Если молоко прокисло, значит надо его пить
- Если появились статьи о вреде курения, надо купить сигарет
- Если появились статьи почему Go плох, значит уже стоит обратить на него внимание
- Если девушка умерла, самое время сделать ей предложение
VBKesha
11.12.2017 12:03PHP плохой язык, на нем пишут кучу народа.
JavaScript плохой, ситуация повторяется.
C/C++ плохой ситуация повторяется.
Это какая то закономерность.
PS. Я не отрицаю что у всего вышеперечисленного есть недостатки…
snuk182
11.12.2017 11:46Go язык утилит и микросервисов. Он не плох. Просто в мире утилит и микросервисов ему очень быстро стало тесно, а авторы так далеко его развитие не планировали.
JekaMas
11.12.2017 13:19Спорно, изначально язык позиционировался, как mainstream язык общего назначения.
snuk182
11.12.2017 15:05Javascript тоже изначально позиционировался, а вышло вон оно как.
Справедливости ради, юзеры просят не многого, но очевидного, без чего mainstream язык обречен:
— Generics
— Управление зависимостями
— Более настойчивая система ошибокJekaMas
11.12.2017 15:09Да, об этом и Дейв Чейни пишет, разве что упоминает еще и immutability. Что в общем-то требования не заоблачные, но надо понимать, что язык не соответствующим ожиданиям для mainstream сейчас, может оказаться обреченным для входа во многие области — просто потому, что программисты ожидают минимального набора фич для современного языка.
Удручает, что движения в этих направлениях незаметно. Ну разве что dep пилят, но предварительное знакомство не обрадовало.Aleosha
12.12.2017 11:53А что не так с godep, кроме нестандартного формата в сравнение с Glide?
JekaMas
12.12.2017 12:02Что лично я наблюдал где-то месяц назад: медленный (https://github.com/golang/dep/blob/master/docs/FAQ.md#why-is-dep-slow), клонирует по новой зависимости, если в них есть обновления. Если добавят кэширование, то будет приличнее.
Второе — сырой, иногда валится без причины.
YetAnotherSlava
12.12.2017 00:10Javascript изначально предназначался, чтобы заставить обезьянку на веб-страничке прыгать.
Прошло много лет, и миллионы обезьянок действительно прыгают и стараются — одни код пишут, другие код ломают, третьи делают инструментарий — красота!
VolCh
12.12.2017 08:41Похоже не просто микросервисов, а инфраструктурных микросервисов. Писать на нём микросервис начисления процентов по счетам клиентов со множеством стратегий рискованно, по-моему в перспективе лет на 5-10, если стратегии появляются и изменяются не реже раза в месяц.
wawa
11.12.2017 11:58-1Странный вывод для такой бурной критики:
Его просто читать и просто использовать.
Разве не это главное для синтаксиса?
Он крайне многословный, невыразительный и плох для умных программистов.
Кмк это всегда баланс между читаемостью и выразительностью.
С одной стороны я хочу как можно лучше выразить идею в коде, с другой — чтобы язык не позволял вливать в код все оттенки личного чувства прекрасного каждого программиста (иначе задолбаешься разбираться в чужом коде или кто-то — в твоем).
Этот баланс конечно тоже лично у каждого свой (если конечно о нем задумаешься), но в Go он уж слишком смещен втопорность«читаемость».
dmbreaker
11.12.2017 12:30По личному опыту нужно минимум три месяца fulltime разработки, чтобы вникнуть в язык и начать в нем разбираться, а тут типичное «я пишу на языке XXX, а Go не работает так, как XXX. Значит Go плохой/кривой».
Это прям как лакмусовая бумажка.
Те, кто с языком работал основательнее, резонно замечают, что есть только проблема с дженериками, остальное — сделано довольно хорошо.0xd34df00d
12.12.2017 00:47Я с Go не работал основательно, так что стало интересно: какое откровение за N месяцев фуллтайм-разработки на Go должно на меня снизойти, чтобы я понял, что обработка ошибок в Go сделана правильно?
Aleosha
12.12.2017 11:58+2Пишу на Go вот уже полтора года. Мне уже можно сказать, что он кривой по всем пунктам?
TheShock
12.12.2017 21:14+1Те, кто с языком работал основательнее, резонно замечают, что есть только проблема с дженериками, остальное — сделано довольно хорошо.
Лол. У него проблем выше крыши. Вот недавно в комментарии описывал, что в нем совершенно уродские теги/аннотации/декораторы, словно их придумывал человек с сильным отставанием в развитии. Это ведь не относится к Дженерикам? ;)
dmbreaker
11.12.2017 12:37-1Кстати, если человек говорит, что в Go нет инноваций — он Go учил два дня и языка не знает.
Интерфейсы в Go — вот главная инновация, я бы сказал козырь перед другими языками.l4l Автор
11.12.2017 12:54+1Выгллядит как haskell's typeclass или abstract class в C++, interface в Java/Delphi/PHP/остальных ООП языках
khim
11.12.2017 17:18Серьёзно? В каких из этих языков реализация интерфейса пишется независимо от самого интерфейса и, в принципе, может быть «залита» в VCS до того, как туда попадёт описание самого интерфейса?
О том, что GNU C++ signatures (которые и явились прототипом интерфейсов в Go) «срисованы» с Haskell type classes — сами разработчики говорят. Но вот «abstract class в C++, interface в Java/Delphi/PHP/остальных ООП языках» — это совсем о другом.SirEdvin
11.12.2017 17:25+1Серьёзно? В каких из этих языков реализация интерфейса пишется независимо от самого интерфейса и, в принципе, может быть «залита» в VCS до того, как туда попадёт описание самого интерфейса?
Ну, вот с Вики список: Prolog, D, Perl, Smalltalk, Python, Objective-C, Ruby, JavaScript, TypeScript, Groovy, ColdFusion, Boo, Lua, Go.
И это, скорее всего, далеко не все. И компилируемые среди них таки есть.
Это называется утиная типизация и в этом тоже нет ничего нового.
khim
11.12.2017 18:29Я и не говорил, что это что-то суперновое. Но в тех вот языках, о которых автор перевода говорит (C++/Delphi/Java/PHP) — этого нет. Есть только в «остальных ООП» языках, но в «остальных ООП», есть, как бы, вообще всё…
mayorovp
11.12.2017 21:36Я бы этот момент рассматривал как небольшую странность а не как ключевую особенность.
Ну вот ни разу не было необходимости писать реализацию интерфейса раньше его самого…khim
11.12.2017 22:13Я бы этот момент рассматривал как небольшую странность а не как ключевую особенность.
Если это не «ключевую особенность», а «небольшая странность», то почему вокруг добавления подобного подхода в C++ сломано столько копий?
Ну вот ни разу не было необходимости писать реализацию интерфейса раньше его самого…
Вопрос не в написании реализации интерфейса раньше описания интерфейса, а во вполне эквивалетной этому возможности по реализации интерфейса, на описание которого вы, при написании вашего класса, вообще не смотрели. Какие-нибудь интерфейсы типа BasicLockable или Swappable, как правило, оказываются реализованы без явного намерения их реализовать.TheShock
11.12.2017 22:18А как вы поймете, что
lock
иunlock
связаны с тредами, а не совершенно другая логика классаGate
?khim
11.12.2017 22:25А как вы поймете, что lock и unlock связаны с тредами, а не совершенно другая логика класса Gate?
Никак — и в этом весь смысл. Совершенно неважно: захватываетlock
какой-нибудьmutex
или тыкву. Важно, чтоlock
— захватывает ресурс, аunlock
— его освобождает. Для класса, которому нужен BasicLocable — этого достаточно.TheShock
11.12.2017 22:35Почему вы думаете, что
lock
— захватывает, а не замыкает дверь? Ну в том плане, что у него бизнес-логика совершенно из другой области, никак не связанная с тредами. И unlock срабатывает только если у пользователя есть права доступа к двери.khim
11.12.2017 22:58Ну я всё-таки исхожу из идеи что программу пишет не макака. И если произошла подобная коллизия, то передавать обьект туда, где он не уместен разработчик не будет.
Речь же не идёт о том, чтобы писать программы случайным образом тасуя компоненты — это и на любом другом языке к добру не доведёт — а о том, чтобы писать программы с удобством.
Так вот описывать интерфейсы там, где они потребляются и не заставлять из описывать там, где они реализуются — просто удобно.TheShock
11.12.2017 23:06Вам удобнее сам факт того, что вы не пишете
implements BasicLocable
и в этом ваше удобство?khim
12.12.2017 00:47То что я не пишу
implements BasicLocable
— это фигня. Главное — что я при этом не вытаскиваю и не включаю в проект модуль, где этотBasicLocable
описан.
То есть я могу поддержать все интерфейсы требуемые MySQL'ем, PostgreSQL'ем и, скажем, Oracle'ом одновременно — и не втягивая в проект библиотек MySQL'я, PostgreSQL'я и Oracle.
Причём для этого мне не нужно «городить огород» с генераторами фабрик, как в Java, а несоотвествие типов таки будет выявлено в момент сборки, а не в рантайме.mayorovp
13.12.2017 09:56Скажите, а как вы будете свою поддержку MySQL, PostgreSQL и Oracle тестировать не втягивая в проект их модули?
khim
13.12.2017 16:09Так же, как в ядре любой операционки тестируется поддежка железа, которого на столе у разработчика нет.
Там и тогда, где это можно использовать — там можно и протестировать.
Вы не поверите, но в природе существуют ещё программисты, способные написать код так, что его не требуется перетестировать по 100 раз после простейших изменений.
0xd34df00d
12.12.2017 00:55В соседнем треде Rust обсуждали, я там пример придумал про тайпклассы и концепты, а тут он пригодился, хорошо-то как! В общем, как вы отличите Forward iterator от Input iterator? Как вы на уровне концептов гарантируете, что он многопроходный?
khim
12.12.2017 01:11Никак — и да, это может быть проблемой. Ну так система типов и не обещала отловить все ошибки.
На практике, впрочем, с подобными проблемами (когда у вас не просто есть два разных интерфейса с одинаковыми сигнатурами, но и из ещё реально нужно различать в рантайме) приходится сталкиваться редко. А вот необходимость опционально поддержать разные вещи — встречается часто. Потому Go «заточен» под второе, а не под первое.0xd34df00d
12.12.2017 19:45Я тут ещё подумал и понял вот что. А чего мы пытаемся избежать? Только ли втягивания зависимости от модуля, объявляющего тайпкласс, как вы где-то рядом писали?
khim
13.12.2017 08:52Ну теоретически — там много слов и размахивания руками, а практически — да, самое важное — это уменьшение количества зависимостей между модулями.
Модуль использующий интерфейс обязан про него знать (иначе, как бы, непонятно как его использовать). Модуль
предоставляющий интерфейс — не обязан (программисту про него знать, в общем, полезно — но тоже не всегда обязательно).0xd34df00d
13.12.2017 23:14Хорошо, но ведь уменьшение количества зависимостей — это не самоцель, не так ли?
И я такую цель ещё бы понял, например, в мире плюсов, где не везде есть адекватный пакетный менеджер, и где зависимости — это кровь, ад и погибель (правда, понял я это, только придя в кровавый тырпрайз с рхелом вместо ОС и вот этим всем, у меня на моей генточке всё было хорошо, даже когда нужно было получающиеся бинарники с библиотеками давать клиентам). Но если у вас адекватный менеджер зависимостей в рамках вашего языка, то в чём проблема?khim
14.12.2017 08:11Но если у вас адекватный менеджер зависимостей в рамках вашего языка, то в чём проблема?
Проблема в лицензиях. Может в вашей домашней «генточке» это и нормально, когда upgrade одного компонента вытягивает пяток других, которые ему нужны только, чтобы из них вынуть описание нужно им интерфейса.
Но в больших компаниях принято на каждый новый компонент полочать «добро» от лигала (и не только). То есть если очередная версия захочет вытащить клиента MS SQL только для того, чтобы реализовать описанный в этом клиенте интерфейс — то вам придётся получать добро на добавление в ваш репозиторий этого самого клиента MS SQL!
Что может растянуться на недели и месяцы.
mayorovp
14.12.2017 09:08Погодите. Вы сейчас говорите о прямой зависимости или о транзитивной?
С прямой все понятно — если нужно реализовать интерфейс для MS SQL — значит, надо добавлять зависимость от их клиента. Это нужно даже в go, потому что во всех случаях кроме самых тривиальных понадобится описание структур данных.
Что же до транзитивной — то тут вовсе не обязательно выкачивать все подобные зависимости. В тех же Java или C# если вы не пользуетесь теми классами которые реализуют интерфейс MS SQL — то и описание интерфейса тоже не нужно!VolCh
14.12.2017 12:31С прямой все понятно — если нужно реализовать интерфейс для MS SQL — значит, надо добавлять зависимость от их клиента.
Нарушает DIP в целом. MS должна предоставить отдельный пакет с интерфейсом и отдельный пакет с реализацией этого интерфейса. Эх, где этот идеальный мир...
alexeykuzmin0
12.12.2017 18:52По уму, у forward iterator должен быть конструктор копирования, а у input iterator — только перемещения. Тот факт, что input iterator, формально, требует наличия конструктора копирования, объясняется историческими причинами — input iterator появился раньше, чем move semantics
khim
12.12.2017 19:05По хорошему-то нужно делать то, что отлично делает perl и хорошо делал python (пока они там все белены не обьелись) — что-нибудь типа
using C++20;
Чтобы порождался код совместимый со старыми обьектными модулями, но при этом на уровне исходного кода какие-то фичи бы выключались.
Тогда от них можно было бы отказываться постепенно…
А так — каждый новый стандарт делает вид, что он существует «в вакууме», старых программ «типа нет» — но при этом мы всё время о них думаем… Шизофрения какая-то…alexeykuzmin0
12.12.2017 19:24А флаги компилятора вам чем не угодили? То же самое ведь практически — единственное, нельзя их на часть файла включить, только на весь.
khim
13.12.2017 08:48Флаги компилятора не угодили тем, что они не являются частью стандарта.
Тем самым они не дают возможности постепенно убирать из него фичи: вначале делаем возможность фичу выключить, потом делаем возможность её включить, но выключаем по умолчанию, потом — отказываемся от неё совсем.alexeykuzmin0
13.12.2017 10:39+1Честно говоря, не понял, зачем флагам быть частью стандарта. Флаги вроде "-std=c++17" делают ровно то же самое, что и гипотетический
в начале файла.using C++17;
khim
13.12.2017 16:13Флаги всего лишь включают режим поддержки соответствующего стандарта. Сам же исходник ровно никак не указывает на то, какой стандарт в нём используется. Потому в новых версиях стандарта нельзя убирать возможности — изменение опций компилятора (которое в подавляющем большинстве систем сборки не делается на уровне одного файла — только на уровне одной библиотеки, а иногда и на уровне одного проекта) не должно приводить к тому, что код вдруг перестанет компилироваться.
0xd34df00d
12.12.2017 19:44Как завезут модули, так что-то такое даже может иметь смысл в контексте плюсов, да.
0xd34df00d
12.12.2017 00:52Какие-нибудь интерфейсы типа BasicLockable или Swappable, как правило, оказываются реализованы без явного намерения их реализовать.
И это сильно не всегда хорошо.
mayorovp
12.12.2017 07:07Увы, но разные разработчики зачастую выбирают разные имена для одних и тех же методов. После чего все идеи насчет «интерфейсы оказываются реализованы без явного намерения их реализовать» ломаются.
0xd34df00d
12.12.2017 19:35Кстати, да, тоже интересное замечание, в каком-то смысле обратное моим претензиям к такому утиному подходу.
Nikita_Danilov
11.12.2017 22:11Можно повернуть вопрос и по другому — а в каких языках отказались от любых вариаций множественного наследования? И тогда задуматься — а может отказались с какой-то целью?
Хотя, справедливости ради, мне рыдать хочется что теперь реализации методов по-умолчанию привносят в интерфейсы C# :( в Java они уже давно имеются.
0xd34df00d
12.12.2017 00:51В каких из этих языков реализация интерфейса пишется независимо от самого интерфейса и, в принципе, может быть «залита» в VCS до того, как туда попадёт описание самого интерфейса?
А оно надо? Я бы предпочёл, чтобы компилятор имел возможность проверить, всё ли я определил и тайпчекается ли оно, в точке определения реализации интерфейса.
А вообще это сурово пахнет концептами C++, а соответствующий пропозал появился до релиза Go, ЕМНИП.
В гошных тайпклассах есть, кстати, multiparam type classes? Fundeps?
khim
12.12.2017 01:31А оно надо? Я бы предпочёл, чтобы компилятор имел возможность проверить, всё ли я определил и тайпчекается ли оно, в точке определения реализации интерфейса.
Ну значит вам больше понравятся другие языки. У обоих подходов есть плюсы и минусы.
А вообще это сурово пахнет концептами C++, а соответствующий пропозал появился до релиза Go, ЕМНИП.
Я бы сказал, что Go'шный ООП очень сильно пахнет GNU C++ сигнатурами, которым, если бы они были людьми, уже можно было пить и курить — это ж почти четверь века назад было всё релизовано! Не исключу, что и раньше, чем в Haskell подобные вещи пробрались, но точно не скажу — это нужно целое расследование проводить…
И да — некая схожесть с концептами и type class'ами наблюдается, но в целом — это не совсем то.
В гошных тайпклассах есть, кстати, multiparam type classes? Fundeps?
Вроде бы нет, но на 100% не уверен.0xd34df00d
12.12.2017 19:57Ну значит вам больше понравятся другие языки. У обоих подходов есть плюсы и минусы.
Ну, я вообще большой фанат того, чтобы компилятор давал мне по рукам и при этом был достаточно умным.
Не исключу, что и раньше, чем в Haskell подобные вещи пробрались, но точно не скажу — это нужно целое расследование проводить…
Придуманы они были не позже 89-го года, вот в этом папире (вообще забавное чтиво с высоты почти тридцати лет). Судя по ссылкам на странице с описанием сигнатур, они появились году в 95-м.
Но это я так, из любви к искусству.khim
13.12.2017 09:04Судя по ссылкам на странице с описанием сигнатур, они появились году в 95-м.
Нет, точно раньше. В GCC 2.6.3 (самая старая версия GCC с поддержкой C++ на ftp.gnu.org) они уже имеются. Это 1994й. А дальше история становится туманной.
quasilyte
12.12.2017 00:58Я хотел бы заметить, что в случае C++ добавив в класс [тип T] виртуальный метод, у вас каждый экземпляр типа T будет содержать таблицу виртуальных методов.
В случае Go сам тип T, реализующий интерфейсы, таблицы виртаульных методов не содержит никогда, поэтому когда мы не используем полиморфное поведение, не будет лишнего жира. Когда T передаётся как интерфейсный тип, вот там уже будет боксинг с аллокацией.
Насколько это хорошо или плохо — не хочу полемизировать, но подчеркнуть, что это не «Как в C++», всё-таки захотелось.humbug
12.12.2017 01:27Чойта?
-fdevirtualize
и-fdevirtualize-speculatively
в компиляторахC++
придумали не вчера.quasilyte
12.12.2017 01:45Возможно мне действительно не хватит знаний продолжить диалог, но стало любопытно: а если член структуры является полиморфным типом, разве компилятор сможет в каких-то случаях избежать перерасхода памяти?
Про спекулятивную девиртуализацию поищу.
Anyway, в Go можно явно сказать: «Тут мне полиморфный тип, а тут просто структру T».
В C++ же полиморфность является свойством самого типа T. Right?khim
12.12.2017 02:16Возможно мне действительно не хватит знаний продолжить диалог, но стало любопытно: а если член структуры является полиморфным типом, разве компилятор сможет в каких-то случаях избежать перерасхода памяти?
Нет. Таблица виртуальных методов будет прописана всегда, конструкторы/деструкторы тоже будут вызываться всегда.
В любом случае тут как бы «небольшие накладные расходы всегда» vs «никаких накладных расходов если полиморфность не нужна, но весьма внушительные — если нужна».
Было бы интересно сравнить на реальных задачах, но на маленьких примерах всё очевидно, а большой проект для этого делать дважды вряд ли кто будет…
humbug
12.12.2017 02:34Anyway, в Go можно явно сказать: «Тут мне полиморфный тип, а тут просто структру T». В C++ же полиморфность является свойством самого типа T. Right?
Угу.
-fdevirtualize
может вырезать лишний жир, если полиморфное поведение не используется. Но… это соооовсем не идеально.
KirEv
11.12.2017 14:04Почти все комменты прочитал, и никто не задался вопросом актуальности переведенной статьи, а ей, на минутку, в марте исполнится три года!
Go использую с версии 1.8, не знаю что было раньше, но то что сейчас — хорошо работает.
Программы, в основном, получаются легко читаемые, а писать небольшие утилитки — в радость, а с горутинами — так вообще шик.
Порог вхождения не высок, но месяца за три — только начинаешь въезжать в идею создания Go, хотя первый микросервис удалось написать дня через два после первого `Hello world`
Вообще не понимаю этих обсираний, особенно от людей, толком не разбирающихся в обсираемом языке.SirEdvin
11.12.2017 14:52А что из описанного в статье поменялось?
Ну и
но месяца за три — только начинаешь въезжать в идею создания Go,
— это что-то дофига, на уровне с erlang и haskell. Вроде же все наоборот утверждают, что Go очень простой.khim
11.12.2017 17:20Вроде же все наоборот утверждают, что Go очень простой.
А тут другая проблема: он простой — но другой. Человек, не видевший ничего, кроме GW-BASIC'а его легко освоит. А людям, работающим на других языках, приходится переучиваться. Это занимает время.SirEdvin
11.12.2017 17:22Позвольте с вами не согласится. Если он другой и на него нужно переучиваться, значит он не простой.
Вот python простой, что бы начать писать на нем какой-то рабочий код достаточно вводной статьи и 15 минут времени. Да, качество кода будет так себе и все прочее, но в целом оно неплохо работает.
Я в течении этого времени только искал, как же нормально задать переменную окружения навсегда.
khim
11.12.2017 18:36Если он другой и на него нужно переучиваться, значит он не простой.
Нет. Так у вас все языки, кроме C++, сложными станут. Потому что с ним я работаю последние 10 лет и мне в нём всё понятно. А чтобы освоить даже Lua — потребуется время.
Простота/сложность языка определяется временем, которое требуется на его освоение человеком, который не знает ни одного языка программирования. С этой точки зрения — Go и JavaScript просты. А C++ и Haskell — сложны.
А вот если вы уже имеете опыт работы на другом языке программирования — то тут ситуация может немного другой оказаться.
Вот python простой, что бы начать писать на нем какой-то рабочий код достаточно вводной статьи и 15 минут времени. Да, качество кода будет так себе и все прочее, но в целом оно неплохо работает.
Та же самая ситуация и с Go, как бы.
И в обоих случаях вам потребуется несколько месяцев, чтобы начать писать идеоматичный код. Поначалу вы будете писать код в стиле того языка, который использовали до того — и плеваться, когда будете упираться в разные «странности».
Я в течении этого времени только искал, как же нормально задать переменную окружения навсегда.
Что значит «задать переменную окружения навсегда», как это сделать в Python'е и почему в Go это сделать сложнее?SirEdvin
11.12.2017 18:38Что значит «задать переменную окружения навсегда», как это сделать в Python'е и почему в Go это сделать сложнее?
Это я про GOPATH и GOROOT. Вот в ubuntu это все еще не делается из под коробки, а значит надо устанавливать самому. А я в то время не знал как.
А в python просто,
pip install package
и погнали. Потом, конечно, больно, но все же можно)khim
11.12.2017 22:34А в python просто, pip install package и погнали.
Серьёзно? А давайте я вам SunOS какой-нибудь постарше дам питона — и вы на нейpip install package
без установки PYTHONPATH и PYTHONHOME погоните. А я посмеюсь.
Или, если SunOS не нравится — могу дать MacOS и скрипт на Python3.
Это я про GOPATH и GOROOT. Вот в ubuntu это все еще не делается из под коробки, а значит надо устанавливать самому. А я в то время не знал как.
И что — это делает язык сложным? Python точно так же требует установки переменных окружения. Просто в Ubuntu это уже сделали за вас, на этапе сборки, собственно, питона.SirEdvin
12.12.2017 00:23Почему так же не сделать для го то?
quasilyte
12.12.2017 01:07Возможно проблема в том, что у Go нет своего pip.
Go build и то, что близ него — это довольно низкоуровневые вещи.
Мне не очевидно, насколько github.com/constabulary/gb юзабелен, но скорее всего со временем появится хороший project-based тул, который станет тем самым pip для Go.
0xd34df00d
12.12.2017 00:58Простота/сложность языка определяется временем, которое требуется на его освоение человеком, который не знает ни одного языка программирования. С этой точки зрения — Go и JavaScript просты. А C++ и Haskell — сложны.
У меня отец физик, и ему хаскель (и интуитивный подход к вычислению программы как к редукции графа) будет сильно понятнее, чем Go или JS. Хотя его опыт ограничивается лабами на фортране под сорокет лет назад.
khim
12.12.2017 01:35У меня отец физик, и ему хаскель (и интуитивный подход к вычислению программы как к редукции графа) будет сильно понятнее, чем Go или JS.
Вы бы ещё про математиков вспомнили!
Да, есть люди, которые функциональный подход воспринимают легче, чем императивный. Но где-то для 90% программистов (и, боюсь, для 98%-99% людей вообще) иперативная «поваренная книга» воспринимается проще.
Даже несмотря на то, что в школе, вроде как, математике учат всех…
ilyasvetozar
11.12.2017 15:18По моему, в статье автор не высказал никакой причины, чтобы считать язык отстоем.
Это скорее делается акцент на эмоции. Если GO является для него простым и для дураков, то наверно у него проблемы с усвоением синтаксиса какого-то сложного языка. Лично по моему С++ и Java также являются простыми и не сложными языками, это дело привычки и опыта.
От языка требуется чтобы он решал те или иные задачи, не важно как, с помощью дженериков как в С++ и Java, или с помощью интерфейсов как в GO.poxvuibr
12.12.2017 08:15Лично по моему С++ и Java также являются простыми и не сложными языками, это дело привычки и опыта.
Насчёт С++ есть просто отличное выступление Скотта Мейерса на конференции, посвящённой языку программирования D. Обязательно посмотрите, очень рекомендую. Джава по сравнению с С++ существенно проще, но по сравнению с Go в смысле количества сущностей — конечно сложнее.
leshabirukov
11.12.2017 15:23Язык можно описать как Си с дополнительными колесиками(ориг.: training wheels).
Трёхколёсный велоСипед.Zinkalla
11.12.2017 22:46Нет, обычный(детский) велосипед с дополнительными колесиками. Трехколесный велосипед это больше Visual Basic :)
EvilFox
11.12.2017 20:41+2Вот, к примеру, решение той же задачи на D:
Дочитал до этой строчки и всё стало ясно. Ох уж эти войны за влияние.
Flaker
11.12.2017 22:42Вспомнилась статья: Why Senior Devs Write Dumb Code and How to Spot a Junior From A Mile Away
alexeykuzmin0
12.12.2017 18:59+1Ссылка сломана
Flaker
12.12.2017 20:39Прощу прощения! Спасибо, что подсказали!
hackernoon.com/why-senior-devs-write-dumb-code-and-how-to-spot-a-junior-from-a-mile-away-27fa263b101a
m0Ray
12.12.2017 11:33-1Ой блииин… Я и раньше-то смотрел на этот язык косо, а теперь понятно, что интуиция не подвела.
McAaron
12.12.2017 14:28-2> Ад копирования
Не всегда шаблоны — хорошо.
Во-первых, в результате использования шаблонов будет сгенерировано столько же кода, сколько и в результате использования копирования.
Во-вторых, при использовании копирования копии можно дорабатывать в индивидуальном порядке, что невозможно при использовании шаблонов.
В третьих, компиляция из шаблонов идет гораздо дольше, чем копий.
В четвертых, отладка копий выглядит проще.mayorovp
12.12.2017 14:32Вот только шаблоны раскрывает программа, а все эти копии кода пишутся вручную.
l4l Автор
12.12.2017 14:59+2- Автоматизация нужна, чтобы уменьшать человеческие ошибки, шаблоны — та же степь. Написали правильно — везде правильно, неправильно — везде неправильно
- Тогда в этом случае а) не нужны шаблоны б) обобщение на слишком большой кусок кода (e.g темплейт нужен не на метод, а его половину)
- Согласен, но впрочем шаблоны помощнее инструмент
- Выше веткой обсудили(-ают), если коротко: тоже самое
sumanai
12.12.2017 19:53Написали правильно — везде правильно
С этим можно (и нужно) спорить, так как шаблон можно использовать неправильно, даже если сам по себе он близок к идеалу.
Но самое весёлое в шаблонах- это их изменение…
billyevans
13.12.2017 03:03Во-вторых, при использовании копирования копии можно дорабатывать в индивидуальном порядке, что невозможно при использовании шаблонов.
Это не так. Можно написать generic-реализацию для всех типов и для конкретного типа отдельно.
unxed
13.12.2017 10:23-1Машинисты паровозов негодовали по поводу появления тепловозов/электровозов: управлять в разы проще, порог вхождения в профессию снижался. И где сейчас те паровозы и те машинисты?
Осваиваем запасную профессию, посоны. Пока не поздно. Не, я серьёзно. Эпоха элитарности программистов неумолимо приближается к закату.develop7
13.12.2017 10:50+1Хосспде, да она закончилась с появлением GW-BASIC, или как его там. Да только как-то всё не в коня корм.
SirEdvin
Я бы еще добавил, что в большинстве случаев советуют использовать одну из самых ужасных вещей в программировании на мой взгляд — кодогенерацию.
Hokum
Не такая она и ужасная. Местами очень даже полезная, взять тоже Thrift. Всему есть место, кроме копипасты. Копипасте тоже есть место, но очень редко и в исключительных случаях.
SirEdvin
Он же вроде для междуязыковой компиляции. А зачем такое в рамках одного языка?
Dair_Targ
У нас в проектах java immutables (которая практически кодогенерация) крайне помогает. Кроме того практически весь современный front-end js построен фактически на кодогенерации (babel, es6 support, react jsx)
MooNDeaR
Что ни разу не облегчает отладку)
Dair_Targ
Про java immutables — попробуйте, там всё более чем прозрачно и отладка становится даже легче (!) за счёт легко предсказуемого поведения — речь об иммутабельных структурах всё-таки.
Про babel/es6/jsx — есть же source maps, один раз на проект настроил и отлаживай себе.
SirEdvin
А еще есть lombok. И мне почему-то, кажется, что оба эти проекта подходят под пункт: "Мы боремся с косяками языка".
А с js ситуация еще хуже, потому что там у людей еще и выбора нет.
Dair_Targ
Да, согласен! Но во всех этих случаях использование ванильных языков для чего-то большого приводит к на порядок-два большим затратам усилий и времени.
Psychosynthesis
Современный «front-end js» это настоящий ад, между делом.
Areso
И потом этот «кодосгенерированный» front-end js адски тормозит на клиентских устройствах.
Hokum
Клиент и сервер могут быть написаны на одном языке, но быть независимыми проектами и разрабатываться разными командами. И тогда Thrift будет их стыковать. Т.е. и клиент, и сервер будут зависеть от некоторого третьего продукта, который будет представлять собой набор файлов Thrift.
SirEdvin
Что мешает в таком случае, если использование других языков не планируется, использовать не Thrift, а возможности языка и написать на нем ядро для обоих частей?
Hokum
Ничего не мешает, это просто два разных подхода к решению одной проблемы. В каком-то одном случае будет удобен один вариант, в другом — другой.
Если ядро призвано только синхронизировать интерфейсы взаимодействия, то, возможно, проще будет поддерживать консистентность используя кодогенерацию, так как она обеспечивает меньшую свзяность проектов между собой. А если должна быть еще какая-то общая бизнес-логика, то лучше писать ядро.
SirEdvin
Возможно, вы и правы. Мне кажется, что все-таки удобнее на одном языке, но задачи бывают разные.
Hokum
И я не являюсь сторонником кодогенерации в Go, просто Вы одним махом записали всю кодогенерацию, как нечто ужасное.
А если вернуться к Go, то она еще и реализована довольно не удобно — ее нужно запускать каждый раз в ручную (по крайней мере из того, что я слышал).
Если уж кодогенерацию использовать, то модификация исходников для кодогенерации должна автоматически приводить к перегенерации кода в момент компиляции, а этого в Go нет.
khim
Кодогенерация считалась всеми простой, понятной, и в общем-то часто используемой вещью до появления IDE… которые её приватизировали.
Разработчики Go — не были фанатами IDE и потому использовали «старый стиль», когда кодогенерация является одним из инструментов, а не «табу», с которым борются всеми возможными способами…
SirEdvin
Это где же в современных ide кодогенерация?
Ну и плоха она по ровно одной причине: результат часто плохо читаемый и нужно ещё дополнительно учить инструмент кодогенерации и дрессировать его. А все для того, что бы обойти косяки языка (например, разницу между примитивными типами и объектами в Java).
unsafePtr
Могу вспомнить как минимум resource файл в VisualStudio. Можно статично через диалоговое коно указать путь к файлу. IDE ещё на прекомпиляции проверит что такой файл существует и сгенерирует статичный класс. Очень удобно например вынести все sql файлы в такой ресурс, а потом в месте использования заявки написать что то вроде:
l4l Автор
e.g: IDEA
khim
Просто считается, что когда IDE геренирует тонны бойлерплейта, которые потом вставляются в ваш проект и засоряют его тоннами строк кода, который вы не писали, то это — хорошо. А когда вы сами, руками пишите скриптик, который порождает код — то это ужас, качмар, низ-зя. Не положено вам магией заниматься. Лицензии от гильдии магической потому что нет.
Ну насчёт читабельности — это уж что вы сделаете, то и будет… А что инструмент нужно учить — ну так библиотеки, которыми вы пользуетесь тоже, как бы, правильно использовать без обучения не получится.
SirEdvin
Я так не считаю. Разве что в случае с ресурсными файлами, как мне указали правильно, и, например, настройкой GUI через стороннее приложение.
Но довольно странно защищать кодогенерацию, которую приходится использовать для того, что бы обойти отсутствие дженериков в языке. Или там, которая будет автоматически пробрасывать ошибки наверх, например.
khim
Кодогенерация вместо дженериков в языке — это, несомненно, костыль. Но неясно, насколько велика проблема. Мне, на практике, дженерики не были так уж сильно нужны и особых проблем я от необходимости поддерживать их руками никогда не испытывал. Возможно в тех, проектах, которые делаете вы — ситуация другая, было бы интересно посмотреть на проект, где дженерики занимают столь существуенную часть, что их отсутствие реально мешает.
А вот этого, пожалуйста, не надо. «Автоматическое пробрасывание ошибок наверх» — это то, с чем создатели Go пытаются извести. Можно эту точку зрения любить или ненавидеть, но если вам нужно «пробрасывать ошибки наверх» — то вам точно не нужен Go.
SirEdvin
Потому что вы связываете xml и ваш код. Если вы связываете сторонние сущности и ваш код, тогда вам приходится использовать кодогенерацию. Особенно если эти сторонние сущности еще используются в программах, которые написаны на разных языках.
Опять же, вы приписываете мне какую-то странную приверженность IDE и "магической лицензии", но я просто считаю кодогенерацию не очень удобной и стараюсь ее избегать как можно чаще. Особенно в рамках одного языка, где казалось бы фич языка должно хватить. Но вот господам с Go и Java приходится страдать.
SirEdvin
Не затруднит ли вас показать код популярного проекта, где все ошибки обрабатываются вот ровно на месте и не наполнены кодом вида:
SirEdvin
Берите любой крупных проект на go и попробуйте сделать
grep "interface{}" -R .
в его репозитории. Я не эксперт, но мой взгляд, почти каждыйinterface{}
будет из-за проблемы с отсутствием дженериков. Я вот только что грепнул alertmanager и prometheus. И даже в alertmanager нашлось место этому чуду:JekaMas
Go подход к ошибкам просто не работает, если не ошибусь, товарищи авторы языка об этом в курсе и задача переосмыслить и предложить новый подход к работе с ошибками значится в задачах go 2.0
aamonster
Я всегда считал, что наоборот: код, сгенерированный визардами и т.п. — это плохо. Во первых, если внести в него изменения — при повторной генерации они пропадут. Во вторых, сгенерённый код не должен попадать в version control. В третьих, это вообще что-то вроде obj-файла, в нормальных условиях мне не надо его видеть.
Короче: кодогенерация на этапе сборки — нормально, кодогенерация при написании кода — фу-фу-фу.
Ну а "считается" — вроде как считается, что стандартные инструменты кодогенерации лучше самописных, поэтому мы наворачиваем горы темплейтов в C++ там, где код мог сгенерить скрипт из пяти строк...