Это перевод статьи юзернейма tucnak с Medium, которая получила обширное обсуждение на reddit.com/r/programming.

image
Окей, заголовок действительно несколько громкий, признаю. Cкажу больше: я прусь от громких заголовков, все из-за внимания. В этой блогозаписи я постараюсь доказать тот факт, что Go это ужасно продуманный язык (спойлер: это так). Я уже играюсь с Go уже на протяжении нескольких месяцев, первый helloworld собрал, кажется, в июне. Математик из меня никакой, но с тех пор прошло уже что-то около 4 месяцев и я даже успел залить на Github несколько репозиториев и собрать немного звезд! Стоит также упомянуть, что у меня совершенно нет опыта применения Go в продакшне, так что любые мои слова о «поддержке кода» или «деплое» не стоит принимать за единственноверную истину.

Я люблю Go, я полюбил его как только впервые попробовал его. Я потратил несколько дней на то, чтобы принять идиоматику, смириться с отсутствием дженериков, разобраться с откровенно странным способом обработки ошибок и вы знаете, всеми этими классическими проблемами, так или иначе связанными с Go. Я прочел Effective Go, много статеек из блога Dave Cheney, следил за всеми новостями из мира Go. Я даже могу сказать, что я достаточно активный участник сообщетсва! Я люблю Go и ничего не могу с этим поделать — Go просто замечательный. Тем не менее, я считаю, что Go это ужасный плохо продуманный язык, который делает совершенно не то, что «продает».

Go считается простым языком программирования. Как сказал Rob Pike, они «убрали из языка все, что только можно было убрать», что сделало его спецификацию просто тривиальной. Эта сторона языка просто удивительна: ты можешь выучить основы в течении минут, сразу начать писать реальный код и и в большинстве случаев Go будет себя вести ровно так, как ты ожидаешь. Ты будешь много беситься, но к счастью, все будет работать круто. В реальности все немного иначе, Go это далеко не простой язык, скорее просто плохой. А теперь взгляните на несколько аргументов, которые подтверждают мои слова.

Причина №1. Манипуляции со слайсами просто отвратительны

Cлайсы (это такие навороченные массивы) очень классные, мне безумно нравится концепт и непосредственно реализация. Но давайте на одну секундочку представим, что нам какой-то момент захочет написать с ними немного исходного кода, может быть совсем чуть-чуть. Слайсы это сердце языка, это один из тех концептов, который делает Go крутым. Но все же, давайте все-таки представим, что как-то вовсе вдруг, в перерывах между разговорами о «концептах», мы захотим написать чуток реального кода. Вот то, что нам предлагает в данном случае язык Go:

// Давайте сделаем немного чисел...
numbers := []int{1, 2, 3, 4, 5}

log(numbers)         // 1. [1 2 3 4 5]
log(numbers[2:])     // 2. [3 4 5]
log(numbers[1:3])    // 3. [2 3]

// Интересный факт: отрицательные индексы не работают!
//
// numbers[:-1] из Python не прокатит. Взамен нам предлагают
// делать что-то вроде этой циркопляски с длиной контейнера:
//
log(numbers[:len(numbers)-1])    // 4. [1 2 3 4]

// “Потрясная” читабельность, Мистер Пайк! Хорош!
//
// А теперь давайте допишем шестерку:
//
numbers = append(numbers, 6)

log(numbers) // 5. [1 2 3 4 5 6]

// Самое время удалить из слайса тройку:
//
numbers = append(numbers[:2], numbers[3:]...)

log(numbers)    // 6. [1 2 4 5 6]

// Хочется вставить какое-то число? Ничего страшного,
// в Go есть общепринятая best practice!
//
// Мне особенно нравится это шапито с троеточиями ...
//
numbers = append(numbers[:2], append([]int{3}, numbers[2:]...)...)

log(numbers)    // 7. [1 2 3 4 5 6]

// Чтобы скопировать слайс, ты должен будешь написать это:
//
copiedNumbers := make([]int, len(numbers))
copy(copiedNumbers, numbers)

log(copiedNumbers)    // 8. [1 2 3 4 5 6]

// И это еще не все.

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

Причина №2. Нулевые интерфейсы не всегда нулевые :)

Они говорят нам, что «ошибки в Go это больше, чем просто строки» и что мы не должны обращаться с ними, как со строками. Например, spf13 из Docker сказал об этом во время превосходного выступления на тему «7 common mistakes in Go and when to avoid them».
Они также говорят, что нам стоит всегда возвращать ошибки через интерфейс error (однородность, читабельность, и так далее). Это в точности то, что я делаю в сниппите кода ниже. Ты наверняка будешь удивлен, но эта программа действительно поздоровается с Мистером Пайком, но все ли так, как должно быть?

package main

import "fmt"

type MagicError struct{}

func (MagicError) Error() string {
	return "[Magic]"
}

func Generate() *MagicError {
	return nil
}

func Test() error {
	return Generate()
}

func main() {
	if Test() != nil {
		fmt.Println("Hello, Mr. Pike!")
	}
}

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

Причина №3. Забавное сокрытие переменных

В случае если вы не в курсе, что это такое, то давайте я все-таки процитирую Википедию: “сокрытие переменных происходит тогда, когда переменная определенная в определенном контексте (условный блок, метод, внутренний класс) имеет такое же имя, как и переменная во внешнем контексте”. Звучит толково, вроде как довольно известная практика, большинство языков программирования поддерживают сокрытие и вс окей. Интересно, но Go не исключение, но тут дела состоят несколько иначе: у нас есть оператор :=, который добавляет веселья. Вот как работает сокрытие переменных в Go:

package main

import "fmt"

func Secret() (int, error) {
	return 42, nil
}

func main() {
	number := 0

	fmt.Println("before", number) // 0

	{
		// meet the shadowing
		number, err := Secret()
		if err != nil {
			panic(err)
		}

		fmt.Println("inside", number) // 42
	}

	fmt.Println("after", number) // 0
}

Да, я в курсе, что оператор := создает новую переменную и задает ей значение по типу справа и то, что в соотв. со спецификацией языка это абсолютно корректное поведение. Но понимаете ли, стоит убрать внутренний контекст (фигурные скобки) и код заработает ровно так, как мы ожидаем («after 42»). Интересно девки пляшут, не так ли? В другом случае вам придется играться с замечательными сокрытием переменных.
Следует отметить, что это все не просто забавный пример, который я придумал за обедом, это реальная ловушка, в которую люди время от времени попадают далеко не по своей вине. На этой неделе я рефакторил немного кода на Go и встретил эту проблему дважды. Компиляторам все равно, линтерам все равно, всем все равно, но код работает неправильно.

Причина №4. Ты не можешь передать []struct как []interface

Интерфейсы классные, Pike&Co. продолжают говорить, что они это то, чем является Go: интерфейсы решают проблему дженериков, мы используем интерфейсы для тестов, через них в Go построен полиморфизм. Говорю вам, я полюбил интерфейсы прямо во время прочтения «Effective Go» и продолжаю их любить. Тем не менее, помимо проблемы «этот нулевой интерфейс не совсем нулевой», о которой я говорил чуть ранее, существует другая неприятная проблема, которая сконяет меня к мысли, что в Go нет полноценной поддержки интерфейсов. Простыми словами, ты просто не можешь передать слайс структур (которые удовлетворяют определенный интерфейс) в функцию, которая принимает слайс этого интерфейса:

package main

import (
	"fmt"
	"strconv"
)

type FancyInt int

func (x FancyInt) String() string {
	return strconv.Itoa(int(x))
}

type FancyRune rune

func (x FancyRune) String() string {
	return string(x)
}

// Удовлетворяет любой структуре с методом String().
type Stringy interface {
	String() string
}

// Строка, состоящая из строковых представлений всех элементов.
func Join(items []Stringy) (joined string) {
	for _, item := range items {
		joined += item.String()
	}

	return
}

func main() {
	numbers := []FancyInt{1, 2, 3, 4, 5}
	runes := []FancyRune{'a', 'b', 'c'}

	// Такое не прокатит!
	//
	// fmt.Println(Join(numbers))
	// fmt.Println(Join(runes))
	//
	// prog.go:40: cannot use numbers (type []FancyInt) as type []Stringy in argument to Join
	// prog.go:41: cannot use runes (type []FancyRune) as type []Stringy in argument to Join
	//
	// Взамен они предлагают нам заниматься вот такой циркопляской:
	//

	properNumbers := make([]Stringy, len(numbers))
	for i, number := range numbers {
		properNumbers[i] = number
	}

	properRunes := make([]Stringy, len(runes))
	for i, r := range runes {
		properRunes[i] = r
	}

	fmt.Println(Join(properNumbers))
	fmt.Println(Join(properRunes))
}

Совершенно неудивительно, что это известная проблема, которая даже проблемой-то не считается. Это просто очередная забавная штука в Go, окей? Я очень рекоммендую прочесть соотв статью в Wiki по теме, станет ясно, почему трюк с передаванием []struct как []interface не прокатит. С другой стороны, подумайте об этом! Мы можем это делать, тут нет никакой магии, это просто проблема компилятора. Посмотрите, прямо здесь, чуть выше в коде я это сделал вручную. Почему компилятор Go не может заниматься этим шапито вместо меня? Да-да, явное лучше чем неявное, но все же?

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

Причина №5. Неочевидные циклы «по значению»

UPD: Это несколько надуманная проблема, если смотреть со стороны языка. А если не смотреть, то все равно, т.к. мы же говорим о косяках непосредственно языка, а не документации и экосистемы.

Это вообще первая проблема, с которой я когда-либо столкнулся в Go. Окей, у нас тут есть «for-range» цикл, который позволяет ходить по слайсам и слушать каналы. Его используют повсюду и это окей. Вот вам еще одна незначительная, но не очень приятная проблема, с которой сталкивается большинство неопытных неофитов: range-цикл поддерживает итерирование только «по значению», он просто копирует значения и ты ничего не можешь с этим поделать — это тебе не foreach из С++.

package main

import "fmt"

func main() {
	numbers := []int{0, 1, 2, 3, 4}

	for _, number := range numbers {
		number++
	}

	fmt.Println(numbers) // [0 1 2 3 4]

	for i, _ := range numbers {
		numbers[i]++
	}

	fmt.Println(numbers) // [1 2 3 4 5]
}

Прошу отметить, я не ругаюсь на то, что в Go нет range-циклов «по ссылке», я ругаюсь на то, что эти range-циклы довольно неочевидны. Глагол “range” будто говорит “пройтись по элементам”, но не очень говорит “пройтись по копиям элементов”. Давайте посмотрим на секцию For из “Effective Go”, в ней ни слова про то, что range-циклы копируют значения слайса, там просто об этом не написано. Я согласен, что это очень незначительная проблема, к тому же я в свое время с ней очень быстро совладал (в течении минут), но неопытный гофер может потратить некоторое время на отладку кучка кода, не понимая, почему значения в массиве не меняются. Нужно было бы хоть немного, но обрисовать это дело в “Effective Go”.

Причина №6. Сомнительная строгость компилятора

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

К Go1.5 нам подогнали классное изменение языка: наконец-то можно указывать элементы словаря без явного указания типа хранимого значения. Мальчикам из Go team понадобилось чуть более пяти лет, чтобы сообразить, что явное указание типа может быть чуток избыточным.

Другая замечательная штука, от которой я прусь в Go — это коммы. Видете ли, в Go у нас есть так называемые многострочные блоки объявления (import / var / const):
import (
    "fmt"
    "math"
    "github.com/some_guy/fancy"
)
const (
    One int = iota
    Two
    Three
)
var (
    VarName int = 35
)

Окей, но как только дело доходит до «читабельности», Роб Пайк решает, что надо ВНЕЗАПНО добавить запятые. В какой-то момент после добавления запятых, он решает, что стоит оставлять запятую на последней строчке списка. Таким образом, вместо того, чтобы писать так:
numbers := []Object{
    Object{"bla bla", 42}
    Object("hahauha", 69}
}

Мы вынуждены писать так:
numbers := []Object{
    Object{"bla bla", 42},
    Object("hahauha", 69},
}

Я до сих пор задаюсь вопросом, почему мы не можем просто обойтись без запятых на блоках import / var / const и уж никак не можем на списках или словарях. В любом случае, Роб Пайк точно шарит лучше меня, так что все окей. Да здравствует Читабельность!

Причина №7. Кодогенерация в Go это просто костыль

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

func Blabla() {
    // code...
}

//go:generate toolname -params -blabla

// code...

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

Эпилог


Как вы видите, я не ругался по поводу обобщений / обработки ошибок / сахара и других классических проблемах, о которых говорят в контексте Go. Я согласен, что дженерики это вовсе не мастхев, но тогда дайте нам нормальную генерацию, а не волшебные циркопляски в комментариях, которые типа как генерируют код. Если в забираете у нас исключения, то пожалуйста, дайте нам возможность нормально сверять нулевые интерфейсы с нулем! А если вы у нас «неоднозначный в плане читабельности» сахар забираете, то дайте возможность писать код, который работает ожидаемо, без «гоп ца ца» сокрытия переменных.

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

Пссс, парень, не хочешь форкнуть Go?

Комментарии (280)


  1. divan0
    29.10.2015 02:41
    -79

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

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


    1. namespace
      29.10.2015 02:46
      +41

      numbers = append(numbers[:2], append([]int{3}, numbers[2:]...)...)
      

      И это необходимый минимум, чтобы вставить элемент в массив. Ты все еще считаешь, что Go совершенен?


      1. evnuh
        29.10.2015 02:53
        -2

        А кто сказал, что вставка элемента в середину непрерывного куска памяти — тривиальная задача?


        1. lair
          29.10.2015 02:54
          +47

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


          1. evnuh
            29.10.2015 03:00
            -2

            Ну потому что это 3 операции на самом деле, а не одна:

            s = append(s, 0)
            copy(s[i+1:], s[i:])
            s[i] = x
            

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


            1. lair
              29.10.2015 03:03
              +17

              Это технически три операции, а семантически — одна. Что мешало сделать insert(numbers, i, v)?


              1. divan0
                29.10.2015 03:11
                -34

                Тем, что городить новый keyword ради спорной нужности операции — это именно то, от чего в Go старались уйти.


                1. lair
                  29.10.2015 03:13
                  +25

                  К сожалению, вся остальная продемонстрированная работа со слайсами тоже не блещет.


                  1. divan0
                    29.10.2015 03:17
                    -40

                    Окей. Пишите на том, где вам блещет, к чему все эти сотни комментариев про «не блещет».


                    1. lair
                      29.10.2015 03:19
                      +23

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


                      1. divan0
                        29.10.2015 04:32
                        -29

                        Конечно из других. Я делюсь знаниями в статьях. А вы в комментариях хейтите то, чем не пользуетесь. Это разные вещи.


                1. creker
                  29.10.2015 03:58
                  +25

                  Почему бы не сделать это в виде библиотечных функций, как это везде повсюду? Как же знаменитый code-reuse? Из языка вырезали так много, что приходится обратно все туда руками заталкивать. И это будет не стандартный API, а велосипед, который все и каждый делают заново. Это — плохо продуманный язык. Кто-то слишком фанател идеей о «спорной нужности операции».


                  1. divan0
                    29.10.2015 04:07
                    -17

                    Приведите три реальных примера, когда Вам нужно засовывать элементы в произвольное место в массиве/слайсе. Реальные кейсы только, пожалуйста. У меня, к примеру, за последние 2 года таких кейсов не было.

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


                    1. Zibx
                      29.10.2015 04:16
                      +16

                      Добавление элемента в сортированный список.


                      1. divan0
                        29.10.2015 04:28
                        -19

                        Если нужен сортированный список — используйте linked list и сортируйте при вставке, слайсы тут причем?
                        Еще примеры? Реальные, из ежедневных проектов, которые вы пишете, желательно.


                        1. Zibx
                          29.10.2015 05:05
                          +11

                          Часть алгоритмов сортировки. Те же деревья (не все они состоят из связанных списков), многие производят манипуляции с массивом в памяти, потому что всё равно всё упрётся в диск. Опять же, связанный список требует дополнительное место на хранение указателя на следующий объект. Массив быстрее связанного списка на ряде задач, например, взятие элемента i в случае массива — операция со сложностью 1, а в случае списка 1-N.


                          1. divan0
                            29.10.2015 05:26
                            -23

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

                            Сколько процентов от вашего основного кода занимает разработка своих структур данных? Оригинальный вопрос в силе — приведите 3 реальных последних примера из своего опыта этой «высокой нужной операции». Вопрос без подвоха.


                            1. Zibx
                              30.10.2015 15:12
                              +1

                              Я пишу на js, но вы правы, это действительно редкая операция, последний раз использовал вставку в середину массива примерно в апреле, когда реализовывал те самые b+tree для серверсайда.


                            1. 0xd34df00d
                              30.10.2015 17:21

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


                              1. Zibx
                                30.10.2015 19:10
                                +1

                                Но относительно циклов или любой другой операции — вставка в середину действительно редка.


                                1. 0xd34df00d
                                  30.10.2015 21:29
                                  +1

                                  Что такое «вставка в середину относительно циклов»?


                        1. Kemet
                          29.10.2015 10:26
                          +1

                          старая изжеванная проблема — LinkedList vs ArrayList
                          Далеко не всегда связные списки хороши, к тому же в памяти гадят, а с учетом, что язык с со сборкой мусора…


                        1. barabanus
                          29.10.2015 21:11
                          +4

                          Забавно, что за более чем десять лет программирования мне ни разу не приходилось использовать linked list ни в геймдеве, ни в контестах, вообще нигде, кроме как в примерах первого учебника по ООП. Уж насколько медленная эта структура, учитывая структуру кэша памяти! Кстати, рекомендую к прочтению «What every programmer should know about memory»


                        1. 0xd34df00d
                          30.10.2015 17:20
                          +5

                          Какая сложность нахождения элемента в сортированном linked list, меньшего или равного данному? Бинарным поиском-то уже не попрыгаешь.

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

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


                    1. VolCh
                      29.10.2015 09:08

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


                      1. divan0
                        29.10.2015 09:16

                        Придумать кейс можно, безусловно. Тут у меня те же вопросы: почему именно слайс и как часто вы пишете свои очереди структур данных? Речь о компромиссах и приоритетах. И да, все еще хочу увидеть по три реальных примера этого «частого важного кейса» из реальной практики.


                        1. VolCh
                          29.10.2015 13:22
                          +4

                          Я просто объединил свои частые кейсы. Реальные примеры, которыми я недавно занимался — очередь заявок на обработку оператором (заявки должны обрабатываться в сложном порядке, новая заявка далеко не всегда вставляется в конец очереди), очередь исполнения бизнес-транзакций по сущности (некоторые транзакции добавляются задним числом), очередь сообщений, пришедших на сокет, которые должны обрабатываться тоже не в порядке поступления. Трех достаточно?


                          1. Blooderst
                            29.10.2015 14:03
                            +1

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


                            1. grossws
                              29.10.2015 14:09

                              Бывает логика приоритетов сложнее, чем в тривиальной priority queue. Взять, например, какой-нибудь deadline scheduling.


                              1. Blooderst
                                29.10.2015 14:20
                                +1

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


                            1. VolCh
                              29.10.2015 14:29

                              Очереди с разными приоритетами был второй этап развития после обычной очереди. В перспективе — очереди, динамически изменяющиеся, а не с определением места при вставке.


                              1. Blooderst
                                29.10.2015 14:37

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


                                1. Ramires
                                  29.10.2015 14:49
                                  +2

                                  связный список тут явно напрашивается

                                  Иногда можно уперется в память, т. к. в linked list хранятся указатели на предыдущий и следующий элементы.
                                  И linked list не поддерживает случаный доступ ( привет, O(n) вместо O(1) )
                                  Стоп, кажется, это уже было сказано здесь
                                  Да все понятно уже


                                  1. Blooderst
                                    29.10.2015 15:03

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


                                    1. Blooderst
                                      29.10.2015 15:08
                                      +1

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


                                    1. Ramires
                                      29.10.2015 15:12

                                      То есть возможность упереться в память при увеличении размера слайса вас не заботит (а это хоть и временное, но двукратное увеличение!)

                                      Не знал, что такая реализация ( это получается vector из C++ ). Тогда замечание про память невалидно.


                                    1. Kemet
                                      29.10.2015 15:39

                                      Такая проблема ArrayList'а есть, она известна, но на практике, бездумное использование ArrayList явно говорит о неверном дизайне. С другой стороны, раз выделив шматок памяти под слайс(с запасом, конечно), мы спокойно вставляем элемент в нужное место — происходит сдвиг элементов, без выделения памяти. Но когда нужен быстрый произвольный доступ — это вариант. Если же у нас миллион элементов в связном списке, то наложится и работа сборщика мусора и отсутствие произвольного доступа и тп.
                                      Где-то лучше связный список, где-то массив, эта тема уже 100500 раз поднималась, и все достоинства и недостатки подходов давно известны. Уже не раз сталкивался с тем, что стараются избавиться от указателей и делать всё на массивах, ибо на но больших объемах сборщик мусора просто не дает нормально работать, что в C#, что в Go. Но спор то не об этом.


                                    1. grossws
                                      29.10.2015 17:26
                                      +1

                                      У связных списков есть другое проблемное место (особенно в concurrent окружении), они сильна трешат prefetch кэша, что может приводить к потере 1-2 порядков. Исключение — это выделение блоками (или одним потоком), чтобы все аллоцированные элементы связного списка лежали последовательно и давали предсказуемый memory access pattern.


                      1. mayorovp
                        30.10.2015 19:14

                        Хм, а почему для таких очередей не использовалась пирамида?


                        1. 0xd34df00d
                          30.10.2015 21:34

                          Потому что профайлер показал, что так медленнее, или бегать по ней потом медленно.


                1. Kemet
                  29.10.2015 10:22
                  +6

                  позаимствованный Oberon-Way не всегда единственно правильный.


        1. creker
          29.10.2015 04:01
          +5

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


          1. gurinderu
            29.10.2015 10:15
            -1

            Я б написал так

            memmove(&a->array[position+1], &a->array[position], &a->array[a->used] - &a->array[position]);
            

            И да, я тоже считаю, что это читабельнее.


            1. mirrr
              29.10.2015 11:00
              +5

              И чем оно читаемее, чем код ниже?

              a = append(a[:position], append([]T{x}, a[position:]...)...)
              


              1. NeoCode
                29.10.2015 11:24
                +9

                идеальный вариант ИМХО

                a.insert(position, value);


                1. defuz
                  29.10.2015 12:00
                  +17

                  Подброшу-ка я дровишек: именно так и выглядит аналогичный код на Rust. :)


                  1. gurinderu
                    29.10.2015 12:02
                    -1

                    Это же vec, а не array


                    1. defuz
                      29.10.2015 12:26
                      +10

                      А чем array, который вы имеете ввиду, отличается от Vec в Rust?


                1. xytop
                  29.10.2015 13:09
                  -1

                  Написал на коленке вариант враппера для Go: play.golang.org/p/OgzWfYeB1V
                  Здесь нет проверок границ и многого другого, но все красиво обернуть при желании тут тоже можно.
                  Конечно другое дело, что все это ложится на плечи конечного программиста: либо писать реализацию самому, либо использовать готовые библиотеки.


                  1. defuz
                    29.10.2015 13:15
                    +17

                    Библиотека для вставки элемента в середину массива – это прекрасно! Сразу вспомнилось это:

                    image


                    1. xytop
                      29.10.2015 13:23
                      +1

                      Ну а что делать?
                      Люди требуют, чтобы было

                      a.insert(position, value);
                      

                      А потом туда можно свистелок напихать еще разных.
                      В C++ std тоже включает много всего разного.


                  1. lair
                    29.10.2015 13:19
                    +1

                    Угу…

                    func main() {
                    	d := []int{1,2,3,4}
                    	s := NewHandySlice(d)
                    	s.Insert(2, "abc")
                    	fmt.Println(s.Values()[1].(int))
                    	fmt.Println(s.Values()[2].(int))
                    }
                    


                    Компилируется (еще бы), но при запуске (очевидно), выдает:

                    2
                    panic: interface conversion: interface is string, not int
                    


                    1. xytop
                      29.10.2015 13:25

                      Я же написал, что нету проверок никаких, это пример обертки.
                      Вы хотите production-ready код?


                      1. lair
                        29.10.2015 13:26
                        +6

                        Я хочу, чтобы проверки были compile-time, а не run-time. Это возможно?


                        1. xytop
                          29.10.2015 13:28

                          В данном варианте нет.


                          1. lair
                            29.10.2015 13:31
                            +3

                            Вот это и проблема. А будет ошибка на Insert или на приведении — уже не так важно.


                            1. xytop
                              29.10.2015 13:44
                              +1

                              Согласен. В С++ это можно было бы решить шаблонами. А вот в С точно так же only runtime checking.


                              1. lair
                                29.10.2015 13:45
                                +12

                                А в некоторых языках просто есть дженерики.


                                1. xytop
                                  29.10.2015 13:57

                                  Может и здесь скоро появится. «Некоторые» языки тоже не сразу их заимели.


                                  1. lair
                                    29.10.2015 13:59
                                    +4

                                    То-то нам (почти) в каждом посте про Го дают ссылку на этот же документ как на объяснение, почему в Го дженериков не будет и это правильно.


                                    1. burjui
                                      30.10.2015 16:20
                                      +3

                                      Андрей Александреску обмолвился о generics в Go в одном из CppCast:
                                      «It became the n-word of Go. Like you can't say 'generics' with 'Go', because everybody is gonna be offended in Go communitiy»

                                      https://www.reddit.com/r/programming/comments/3qs888/cppcast_d_with_andrei_alexandrescu
                                      Для тех, кто не знает, «n-word» — это эвфемизм слова «nigger» в США.


                                      1. tyomitch
                                        31.10.2015 00:40
                                        +1

                                        <занудство>«n-word» — это не эвфемизм слова «nigger», а название слова «nigger». Эвфемизмы — например, «dark-skinned» — используются для обозначения негров, а не слов.</занудство>


                                        1. burjui
                                          31.10.2015 13:58

                                          Точно, спасибо за исправление.


              1. gurinderu
                29.10.2015 13:55
                -2

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

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


                1. defuz
                  29.10.2015 14:02

                  Тот факт, что мы активно обсуждаем операцию insert наталкивает меня на мысль о том, что видимо эти самые array в go являются growable (динамические). Если это так, то это мало чем отличается от векторов в C++ и Rust.

                  В каком языке векторы построены поверх array?


                  1. gurinderu
                    29.10.2015 18:28

                    Скорее всего вы права, можно сравнить go array с rust vect.

                    В Java Vector реализован на Array, в С++ насколько я помню тоже.


                    1. grossws
                      29.10.2015 19:47
                      +2

                      В java Vector — давно устаревшее говно мамонта. А актуальный ArrayList работает поверх массивов, с динамическим адаптивным расширением в зависимости от размера данных. В openjdk8 этот массив может быть либо пустым (константой), либо размером не менее 10 элементов. При небольших размерах он увеличивается с шагом в 50% от текущего размера.


      1. divan0
        29.10.2015 03:10
        -33

        Я не мыслю категориями «совершенен»/«не совершенен», это оставим школьникам. Вставка в произвольное место — это by design дорогая операция, вынуждающая двигать память, но 99% кейсов использования слайсов — это append. Это называется «компромисс», и все что касается дизайна языка — это всегда о компромиссах. То, что в слайсах в Go так неудобно делать произвольную вставку — создает стимул думать головой о том, какая структура данных больше подойдет, если нужно делать много быстрых произвольных вставок, а не вслепую ворочать память.

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


        1. namespace
          29.10.2015 11:31
          +31

          школьник тыкающий в Пайка и Томпсона, «объясняющий» им своим невежством, какой плохой они придумали дизайн — это мне непонятно

          Ваня, ты наверное про нигилизм не слышал. Я не спорю, что Пайк и Томпсон крутые чуваки, но давай они хоть как-то будут объяснять свои решения. Удел разумного человека не идти на поводу у авторитетов, а делать соотв умозаключения, которые приведут его к истине.


        1. KReal
          29.10.2015 15:41
          +16

          Что вы заладили — «школьник», «школьник»… у вас школьник пирожок отобрал, что ли?


          1. andyN
            29.10.2015 21:34
            +4

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


        1. alterpub
          30.10.2015 14:12
          +1

          У вас судя по всему какой-то комплекс, на тему школьников, лол ;)


      1. yul
        29.10.2015 11:26
        +3

        Меня больше всего поразило, что нельзя определить метод для этого. А еще кто-то недавно гнал на Ruby с его monkey-patching…


        1. mirrr
          29.10.2015 12:17

          Я не совсем в теме, почему нельзя?


          1. kekekeks
            29.10.2015 12:38
            +4

            Дженериков нет. Этот метод придётся копипастить для каждого типа, используемого в массивах.


            1. mirrr
              29.10.2015 12:40

              А что мешает использовать интерфейсы?


              1. kekekeks
                29.10.2015 12:42
                +3

                Необходимо определить операцию не над определённым типом, а над массивом таких типов, интерфейсы здесь причём?


                1. andrewnester
                  29.10.2015 12:44
                  +1

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


                  1. kekekeks
                    29.10.2015 12:47
                    +4

                    Рефлексия — это не «не очень круто», это «очень не быстро».


                1. mirrr
                  29.10.2015 13:02

                  Я об []interface.

                  Если существует такая частая необходимость вставки в вашей структуре данных, да еще со множеством различных типов, то почему не сделать тип-обертку над []interface{}

                  Как-то так
                  package main
                  
                  import (
                  	"fmt"
                  )
                  
                  type arr []interface{}
                  
                  func (a *arr) insert(pos int, x interface{}) {
                  	*a = append((*a)[:pos], append([]interface{}{x}, (*a)[pos:]...)...)
                  }
                  
                  func main() {
                  	a := arr{1, 2, 3, 4, 5}
                  	a.insert(2, 77)
                  
                  	fmt.Println(a) 
                   	// out: [1 2 77 3 4 5]
                  }
                  


                  1. lair
                    29.10.2015 13:21
                    +6

                    Если существует такая частая необходимость вставки в вашей структуре данных, да еще со множеством различных типов, то почему не сделать тип-обертку над []interface{}

                    Потому что преимущества строгой типизации теряются.


        1. defuz
          29.10.2015 12:24
          +3

          Э… Всмысле нельзя? Т.е. вообще нельзя? А как жить, если надо? Можете для человека, который вообще не знает Go, коротко объяснить что именно нельзя и почему?


          1. mayorovp
            29.10.2015 12:52
            +3

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


            1. mirrr
              29.10.2015 17:23

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

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


      1. taliban
        29.10.2015 14:04
        -2

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


        1. lair
          29.10.2015 14:22
          +5

          Ты ведь в курсе что можно оборачивать код который тебе не нравится в функции с более красивым дизайном?

          О, а как в Go обернуть в функцию код для вставки элемента в середину слайса произвольного типа (с сохранением типобезопасности, конечно)?


          1. mirrr
            29.10.2015 15:05
            -1

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

            func insert(s interface{}, pos int, x interface{}) {
            	switch a := s.(type) {
            	case *[]int:
            		*a = append((*a)[:pos], append([]int{x.(int)}, (*a)[pos:]...)...)
            	case *[]byte:
            		*a = append((*a)[:pos], append([]byte{x.(byte)}, (*a)[pos:]...)...)
            	case *[]string:
            		*a = append((*a)[:pos], append([]string{x.(string)}, (*a)[pos:]...)...)
            	// case ...
            	}
            	return
            }
            


            1. lair
              29.10.2015 15:08
              +6

              Во-первых, типобезопасность не сохранена — если я передам s одного типа, а x — другого, будет ошибка приведения.
              Во-вторых, не будет работать для произвольного (неизвестного на момент написания функции) типа.


              1. Blooderst
                29.10.2015 15:20
                -10

                В Go УЖЕ есть универсальная встроенная функция манипулирования слайсами, которая работает с любыми типами и с максимальной скоростью — append. И не нужно ничего изобретать, все уже изобретено и реализовано. Если вам не нравится синтаксис — это ни как не проблемы языка. Более того, так и задумано. Неужели вы считаете, что авторы оказались не способны привнести в синтаксис сахарку, если бы посчитали это необходимым? Я вас уверяю, смогли бы. Тот факт, что эта функция реализована именно таким образом говорит о том, что авторы языка считают именно такой синтаксис наиболее подходящим и я лично с ними в этом солидарен. Этот синтаксис показывает программисту, что операция которую он совершает, не простое присваивание, а ресурсоемкая задача. Нравится вам это или нет, но этот язык именно такой. Если он вам не подходит — не пишите на нем. Но не надо говорить, что язык плох из-за того, что он для вас недостаточно подслащен. Расслабьтесь и наслаждайтесь жизнью в сторонке от Go.


                1. lair
                  29.10.2015 15:22
                  +4

                  Спасибо за просвещение.

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


                1. creker
                  29.10.2015 16:14
                  +10

                  Этот синтаксис показывает программисту, что операция которую он совершает, не простое присваивание, а ресурсоемкая задача.

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

                  Расслабьтесь и наслаждайтесь жизнью в сторонке от Go.

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


              1. mirrr
                29.10.2015 17:03
                -2

                Точно, не заметил.

                Задача слишком быстро начала обрастать дополнительными условиями) Началось все с «невозможности» обернуть код вставки выше в метод. Дальше понадобилась одна ф-я для нескольких типов, чтобы не «копипастить» метод, потом строгая типизация, далее произвольные типы и типобезопасность.

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


          1. taliban
            02.11.2015 15:52

            Отвечу Вашими же словами :)

            Спасибо за просвещение.

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

            У человека выше подгорело из-за синтаксиса
            И это необходимый минимум, чтобы вставить элемент в массив. Ты все еще считаешь, что Go совершенен?

            А не из-за того что в go нет шаблонов. Как бы тоже следите за цепью диалога, а потом свои несчастные 5 коп вставляйте.


            1. lair
              02.11.2015 17:08
              +3

              Вот прямо в исходном посте все про это написано: «А так как у нас нет никаких этих ваших дженериков, то написать красивую функцию insert(), которая будет прятать весь этот ужас просто не получится.»

              Для меня это очень логичный ход мысли: уродливый синтаксис — написать что-нибудь, что его прячет — что, мне это писать для каждого типа?! Причем это не выдуманное, я по этой тропинке прыгал во времена .net 1, где напишешь какую-нибудь удобную мулечку, а потом ррраз — и вынужден ее повторять еще для пары разных типов.

              (причем даже когда дженерики случились, тоже было больно, потому что операции сложения они, скажем, не покрывали, и приходилось отдельно прыгать)


              1. taliban
                02.11.2015 17:37
                +2

                Да, придется повторять, с этим то никто и не спорит, но я не пойму, почему не сказать прямо (я напомню я отвечал не напрямую посту, а комментарию) «меня бесит копипаст, не хочу повторять код». Вместо этого приводят просто длинный и уродливый кусок кода со словами «смотри какой уродливый код мне приходится писать?». Об этом речь а не о типе и невозможности написать универсальный для типа код.


                1. lair
                  02.11.2015 17:40

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


                  1. taliban
                    02.11.2015 17:54
                    -1

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


                    1. lair
                      02.11.2015 17:57
                      +2

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

                      Ох, я был бы рад, если бы это было так.


                      1. taliban
                        02.11.2015 19:50
                        +1

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


                        1. mird
                          03.11.2015 10:35
                          -1

                          А потом придет divan0 и скажет, что создатели языка все уже придумали и дженерики не нужны, а этой задачи вообще не существует.


    1. creker
      29.10.2015 03:53
      +9

      Только вот я это читаю и просто диву даюсь — да это ж пересказ всего того мата, который был у меня, когда я начинал писать на Go. Простая примитивная программа из-за тех же слайсов и чехорды между [n]int и []int превращается в такое убожество, что страшно. Язык простой, да, но код на нем получается совсем не простой.


    1. hurricup
      29.10.2015 12:04
      +23

      Во-первых, зачем же ограничиваться обсуждениями школьник/не школьник, давайте обсудим национальность и сексуальные предпочтения автора поста.
      Во-вторых, это перевод.
      В-третьих, если у языка с точки зрения программиста есть недостатки, почему бы ими не поделиться? Если это будет субъективная неприязнь данного программиста, статья сгинет в Лету, если многих это беспокоит, почему не обсудить? Спокойно и конструктивно. После ваших комментов возникат одна мысль — «Бомбануло».


      1. AterCattus
        29.10.2015 13:14
        +7

        Во-вторых, это перевод.

        Это авторский перевод, кстати.


    1. stepanp
      29.10.2015 15:01
      +5

      Судя по традиционному срачу в коментах, Go это новый JS


  1. evnuh
    29.10.2015 02:49
    -43

    Ну окей, человеку не нравятся эти моменты языка, он это рассказал в своём блоге. Но зачем мне-то знать, что ему не нравится?


    1. evnuh
      29.10.2015 13:56
      +16

      Прошу заметить как автор элегантно в первом предложении снял с себя ответственность под видом перевода. А перевод-то своего собственного поста :)


      1. evnuh
        29.10.2015 17:23
        +4

        Решил покопаться ещё. Автор, опять же, сам лично разместил ссылку на реддите на свой же пост: www.reddit.com/r/programming/comments/3qjo3y/why_go_is_a_poorly_designed_language_from_a. И «обширность» обсуждения на реддите спровоцирована не статьёй, а ложными фактами, отсебятиной и враньём в цитатах Роба Пайка у автора (люди потрудились, поискали и выяснили, что он такого не говорил).
        В общем, не стоит попадаться на грязные уловки автора и мнимый авторитет самого себя :)


        1. namespace
          29.10.2015 18:34
          -1

          У меня в статье нет ни одной цитаты Пайка, о чем ты?


          1. evnuh
            29.10.2015 18:38

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


            1. xytop
              29.10.2015 18:44

              Он говорил как-то так:

              ...And yet, with that long list of simplifications and missing pieces, Go is, I believe, more expressive than C or C++. Less can be more.

              But you can't take out everything. You need building blocks such as an idea about how types behave, and syntax that works well in practice, and some ineffable thing that makes libraries interoperate well.

              We also added some things that were not in C or C++, like slices and maps, composite literals, expressions at the top level of the file (which is a huge thing that mostly goes unremarked), reflection, garbage collection, and so on. Concurrency, too, naturally.


            1. namespace
              29.10.2015 18:47

              Ну смотри, там я действительно облажался — я не хотел цитаты, я хотел его несколько перефразировать. Там была ссылка на его статью, где он после списка вещей (огромного списка), которые они из Go убрали, говорит: «But you can't take out everything. You need building blocks such as an idea about how types behave, and syntax that works well in practice, and some ineffable thing that makes libraries interoperate well.»

              Это кагбэ намекаэт, что он пытался убрать все по-максимуму, так что я не очень-то и соврал. С другой стороны — да, нехорошо получилось. Но ничего, я кавычки из оригинала убрал и заменил said на according to, спасибо за фидбэк.


  1. jemali_m
    29.10.2015 02:49
    -46

    Ооо, началось!


    1. jemali_m
      29.10.2015 02:50
      -43

      Ну давай, расскажи мне )


      1. jemali_m
        29.10.2015 03:04
        -41

        ЕЩЕ!!!


  1. leventov
    29.10.2015 04:38
    +1

    Стиль неуместный.


    1. namespace
      29.10.2015 13:15
      -10

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


      1. leventov
        29.10.2015 13:19
        +4

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


        1. namespace
          29.10.2015 15:12
          -25

          Я тебе даже больше скажу: мало того, что я так считаю, я глубоко убежден, что мои выпады на Пайка и украшения в английским — это очень оригинально и обосрацца как остроумно! Кстати, зашел к тебе в профиль минус поставить, а там уже… И заметки нет — не знаю, за что. Не напомнишь?


          1. Utter_step
            30.10.2015 00:18
            +9

            Ну так-то зачем?.. Всё впечатление об адекватности теряется сразу.


    1. NeoCode
      29.10.2015 13:57
      +1

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


      1. leventov
        29.10.2015 16:32
        +2

        Быстрее усвоится то, что не отвлекает на форму, если уж на то пошло.


  1. QtRoS
    29.10.2015 05:13
    -17

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


    1. hurricup
      29.10.2015 12:42
      +1

      Это может ничего не значить. Допустим, у меня есть нагруженный проект. И нужно поправить некий батлнек производительности, максимально оптимизировать. И предположим, что в силу сложившихся обстоятельств наилучшую производительнсть мне дает Brainfuck (обеспечивая необходимую стабильность и не порождая других проблем). Конечно же, я напишу это на нем. Но это не говорит ничего о юзабельности языка. Лишь о том, что у него есть свои сильные стороны и что ситуативно имеет смысл его использовать.

      И это лишь один из вариантов, почему язык %language% используется в %project%.


      1. AterCattus
        29.10.2015 13:37
        +1

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


      1. QtRoS
        29.10.2015 15:54

        Лишь о том, что у него есть свои сильные стороны и что ситуативно имеет смысл его использовать

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


        1. AterCattus
          29.10.2015 16:24

          Конечно бесмысленно пытаться переписывать то, что и так хорошо работает. Go для тех задач, где его особенности проявляют себя как сильные стороны по сравнению с тем же C/C++.


    1. c4boomb
      29.10.2015 15:30
      -8

      Вообще считать ВК авторитетным глупо. Они не использовали ООП при написание такого большого, сложного по структуре и высоконагруженого проекта.


      1. kyrie
        29.10.2015 19:09
        +15

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


        1. Bringoff
          30.10.2015 08:30

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

          <offtop>
          Давненько вы им не пользовались :)
          </offtop>


          1. Bringoff
            30.10.2015 13:22
            -2

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


            1. Alesh
              01.11.2015 15:15
              +1

              никогда не пытался смотреть видео или гифки вконтакте

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


              1. Bringoff
                01.11.2015 17:37
                -2

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


  1. mayorovp
    29.10.2015 07:05
    +6

    Пятая проблема надумана — я не слышал ни про один язык программирования, где были бы циклы foreach «по ссылке» (кроме, наверное, C++ — там я немного отстал от мейнстрима).


    1. salas
      29.10.2015 08:12
      +3

      Аналогичный код на Swift не компилируется: «cannot pass immutable value to mutating operator: 'number' is a 'let' constant». Кажется, автор этого просит, а не циклов по ссылке.


      1. salas
        30.10.2015 05:51
        +2

        Ха. Просто так не компилируется? Да кого это останавливало!

        var numbers = [0, 1, 2, 3, 4]
        
        for var number in numbers {
            number++
        }
        
        print(numbers) // [0, 1, 2, 3, 4]
        


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


    1. lega
      29.10.2015 08:41
      -1

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


      1. mayorovp
        29.10.2015 08:48
        +1

        В питоне объекты всегда передаются по ссылке на них, в го — всегда копируются. Циклы тут ни при чем: play.golang.org/p/B5Zyjj1-R3


      1. salas
        29.10.2015 09:03
        +1

        Но, кстати, приведённый wtf переводится на Python без потерь:

        >>> numbers = [0,1,2,3,4]
        >>> for number in numbers: number += 1
        >>> numbers
        [0, 1, 2, 3, 4]


        1. lega
          29.10.2015 09:18
          +2

          Потому что в питоне (и в некоторых других) простые типы как строки и числа копируются, а словари, массивы и т.п. передаются ссылкой (т.е. копируется ссылка на объект).


          1. salas
            29.10.2015 09:35
            +1

            Нет. Всё ссылка на объект, а вот += может быть вызовом метода __iadd__, а может быть присваиванием этой ссылке нового значения, если у объекта не оказалось этого метода.


            1. mayorovp
              29.10.2015 09:42
              +2

              Вот, кстати, доказательство:

                 a = 5
              => None
                 b = a
              => None
                 object.__repr__(a)
              => '<int object at 0x7fd04cc16700>'
                 object.__repr__(b)
              => '<int object at 0x7fd04cc16700>'
                 b += 1
              => None
                 object.__repr__(b)
              => '<int object at 0x7fd04cc16720>'
                 b
              => 6
                 a
              => 5
              


              PS извиняюсь за минус, не сразу понял о чем вообще речь


              1. funca
                29.10.2015 17:17
                +5

                Конкретно с мелкими int тут другая история. В классическом CPython целочисленные значения от -5 до 256 представляются ссылками на массив констант. Поэтому переменные с одинаковыми значениями в диапазоне [-5, 256] содержат одинаковые ссылки.

                >>> 256 is 256
                True
                >>> a = 256
                >>> b = 256
                >>> a is b
                True
                

                Для остальных целочисленных значений такое условие уже не выполняется:
                >>> 257 is 257
                True
                >>> a = 257
                >>> b = 257
                >>> a is b
                False
                

                Эта особенность поддерживается на уровне операций:
                >>> a = 256
                >>> b = 257
                >>> a is b
                False
                >>> b -= 1
                >>> a is b
                True
                


                1. ZyXI
                  29.10.2015 17:24
                  +3

                  В примере mayorovp ничего не изменится, если вы замените a = 2 на a = 2.0. Данная оптимизация тут совершенно не причём: это иллюстрация к тому, что foo += bar есть foo = foo + bar, если у foo нет метода __iadd__ (в этом случае это будет foo.__iadd__(bar)). Ни у строк, ни у чисел в Python такого нет.


              1. ZyXI
                29.10.2015 17:20
                +2

                Если вам нужен адрес объекта, то вместо object.__repr__(obj) можно использовать id(obj): он быстрее печатается. Правда, не показывает тип, но здесь тип очевиден.


    1. VolCh
      29.10.2015 09:11
      +5

      PHP.


      1. mayorovp
        29.10.2015 09:14
        -1

        Да, точно…


    1. ik62
      29.10.2015 10:30
      +4

      в D программист управляет типом цикла(который может быть и по ссылке и по значению):
      foreach(ref x; y) {

      }
      foreach(x; y) {

      }


    1. bo0rsh201
      29.10.2015 10:39
      +5

      я тут на сцене появляется PHP как пример «совершенного» языка программирования, где это возможно :)
      P.S. опоздал с комментом про PHP


    1. namespace
      29.10.2015 10:44

      Это можно сделать как минимум в C++ и D. Я уверен, что в доброй пачке других языков аналогичное поведение также возможно. Конкретно в данном случае автора смущает не то, нет циклов «по ссылке», а то, что это никак не освещенно в документации.


      1. mayorovp
        29.10.2015 11:27
        +1

        Довольно странно освещать в документации отсутствие некоторой фичи…


        1. 6opoDuJIo
          29.10.2015 11:59

          … но вполне нормально детально изобразить поведение цикла.


        1. VolCh
          29.10.2015 13:28
          +2

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


          1. mayorovp
            29.10.2015 18:32
            +1

            Цитата из спецификации языка:

            For each entry it assigns iteration values to corresponding iteration variables if present and then executes the block.

            assigns — это присваивание. Присваивание — это всегда копия.


            1. grossws
              29.10.2015 19:50
              +1

              Только, если ранее дано такое определение assign. Т. к. присваивание часто подразумевает присваивание ссылочного типа.


    1. grossws
      29.10.2015 12:34
      +1

      У автора просто неудачный пример с int. Если взять что-то вида

      package main
      
      import "fmt"
      
      type A struct {
      	B string
      }
      
      func main() {
      	as := []A{{B: "asd"}, {B: "zxc"}}
      	
      	for _, item := range as {
      		item.B = item.B + "!"
      	}
      	
      	for _, item := range as {
      		fmt.Print(item.B) // asdzxc
      	}
      	
      	fmt.Println()
      	
      	for i, _ := range as {
      		as[i].B = as[i].B + "!"
      	}
      	
      	for _, item := range as {
      		fmt.Print(item.B) // asd!zxc!
      	}
      }
      play.golang.org/p/HCBcUB1Z6y

      И в подавляющем большинстве распространенных языков (C, C++, Java, Scala, Python, Ruby, JS, как минимум) поведение будет отличаться от Go, т. к. поле в структуре поменяет значение.


      1. grossws
        29.10.2015 12:38

        Насчёт C и C++ погорячился, структура может. В смысле цикла foreach. А c for всё и так очевидно.


    1. hurricup
      29.10.2015 12:53
      +1

      Perl5


    1. s_kozlov
      29.10.2015 13:28
      +3

      Эта проблема приятно решена в PHP, например. Добавляешь & и все.

      foreach ($arr as &$value) {
          $value = $value * 2;
      }
      


      Почему бы так не сделать в Go, удобно же.


      1. namespace
        29.10.2015 13:35

        Да, аналогично в С++ и по-моему, в D.


      1. s_kozlov
        29.10.2015 13:38

        P.S. Тоже опоздал про PHP…


      1. VolCh
        29.10.2015 13:50

        В случае массивов объектов они всегда передаются по ссылке (что логично). & нужно ставить только для изменения элементов массивов скаляров (неоднозначно логично или нет) и массивов массивов (не логично).


        1. s_kozlov
          29.10.2015 15:38

          Для Go, по аналогии с PHP, я бы предложил так:

          for _,&v := range arr {
          v=newValue
          }
          

          или так, более согласованно
          for _,&v := range arr {
          *v=newValue
          }
          

          что будет происходить, если arr — слайс интерфейсов или каналов, оставим пока за скобками


  1. dbelka
    29.10.2015 08:19
    +3

    Ещё не понятно почему нельзя было сделать нормальный(!) менеджер зависимостей, ведь есть куча успешных реализаций: npm(nodejs), cargo(rust), composer(php),…


    1. avdept
      29.10.2015 10:02
      +1

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

      github.com/avelino/awesome-go#package-management


    1. namespace
      29.10.2015 10:53

      Много кому (в том числе и мне) нравится go-get, прямо очень-очень нравится. Но так как мейнтейнеры (в итом числе и я) иногда ломают пакеты это не очень класс. Хотелось бы, чтобы была возможность задавать зависимость не только по URL, но еще и по git тегу, как это делает labix.org/gopkg.in


      1. bolk
        29.10.2015 11:58

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


        1. Ivanhoe
          29.10.2015 12:14
          +2

          А с транзитивными зависимостями как? Ну т.е. форкнутый-то пакет тоже от чего-то зависит.


  1. Morthan
    29.10.2015 08:42
    +9

    Меня настолько заинтересовали слова «шапито» и «циркопляска», что я даже не поленился посмотреть, как оно в оригинале. Потом поискал, сколько раз эти слова употреблялись на хабре ранее. Много думал.


  1. Zagrebelion
    29.10.2015 08:58
    +11

    если заглянуть в спецификацию и прочитать, что «a:=42» — это сокращённая запись выражения «var a=42» то третий пример (со скрытыми переменными) перестанет быть таким уж неожиданным.


    1. namespace
      29.10.2015 10:13
      -2

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


      1. gunya
        29.10.2015 12:15
        +8

        Этот пример вообще дико странный, потому что является наглядной демонстрации областей видимости.
        В C++ все точно также, один в один. Если ты открываешь блок { }, то значит, определяешь новую область видимости, значит, если ты определяешь новую переменную — старая не перезапишется. В чем несовершенство языка, скажите мне, пожалуйста?


        1. defuz
          29.10.2015 12:42
          +6

          Наверное все-таки в том, что определние новой переменной и присваивание нового значения визуально мало чем отличаются (привет опечаткам и долгому дебагу).


        1. shuler
          29.10.2015 23:45
          +6

          Вы кажется не осознали всю боль. Откройте пример play.golang.org/p/TNfS7eyYSf и сотрите фигурные скобки, которые задают новую область видимости.
          Код скомпилируется, несмотря на повторное :=
          А вот если еще объявить var err error перед этим, то получим уже

          no new variables on left side of :=


          1. gunya
            30.10.2015 14:14
            +4

            Окей, я осознал всю боль. Доработал пример, чтобы все стало еще больнее play.golang.org/p/U4Xm5dDf52


            1. nwalker
              30.10.2015 16:20

              Вот этот пример прямо шикарный вышел.


            1. grossws
              30.10.2015 18:58

              Лишнее двоеточие в 24 строке. Оно же, поправленное: play.golang.org/p/T6_6naVdv6


              1. gunya
                30.10.2015 19:24
                +1

                Я его намеренно оставил, чтобы сразу огрести проблем на, казалось бы, синтаксически корректном примере.
                C++ в такой ситуации гораздо понятнее бьет по рукам с ошибкой «redeclaration of 'int a'», но в плюсах нет оператора множественного присваивания.


                1. grossws
                  30.10.2015 20:25

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

                  before 0
                  inside 42
                  after 60


    1. 9mm
      29.10.2015 20:07

      В том-же D, shadowing запрещён, что, на мой взгляд, добро. — сам я не редко совершаю ошибки связанные с ним.


  1. Daniro_San
    29.10.2015 09:03
    -9

    Пожалуй скажу немного.
    Go действительно поначалу очень привлекает.
    Меня привлек распиаренностью где бы то ни было, воплями о «языке нового поколения», сочетании (!) Си и паскалеобразного синтаксиса.
    Действительно, операция ":=" выглядела для меня странно…
    Как и объявления типов справа — показалось слишком громоздким.
    Потом становится понятно что это просто очередная пустышка.
    Давайте ка вспомним ту самую цепочку «инновационных языков» зарождавшихся по очереди в недрах Apple для замены Objective-C.
    Припоминаете?
    Неужели сейчас началась та же болезнь у немного другой компании?


    1. andrewnester
      29.10.2015 10:16
      +1

      объявление типов справа кажется громоздким, а слева вполне лаконичное? кол-во символов то одинаковое


      1. Daniro_San
        29.10.2015 10:28
        -1

        Вот пример с Go:

         var v6 *int
        

        А вот с C++:
        int* v6;
        

        У вас есть возможность посчитать символы и подтвердить свои слова.


        1. andrewnester
          29.10.2015 10:41
          +1

          ну вы же сказали именно про типы, а не переменные

          Пример на Go

          func SomeFunc(i int) {}
          
          type MyInteger int
          
          


          Пример на C

          void SomeFunc (int i) {}
          
          typedef int MyInteger;
          


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


          1. Daniro_San
            29.10.2015 10:56
            -2

            Выходит, моя ошибка.
            Я как раз имел ввиду объявление типа переменной.


        1. NeoCode
          29.10.2015 11:15

          Слово «var» добавляется.
          Иногда говорят что так проще для парсера, потому что «var» — заранее известное ключевео слово, после которого может быть только объявление объектов; а «int» — имя типа, которое в общем случае может быть и пользовательским именем типа. В общем это правильно (для функций однозначно правильно иметь ключевое слово «func», «fn» и т.п. в начале), хотя при желании можно сохранить и оригинальный сишный синтаксис для переменных — так как переменные объявляются чаще всего, и для сохранения единообразия с объявлением полей структур.
          Для этого нужно принять что любые конструкции вида «name1 name2» (два идентификатора подряд) это всегда объявление объекта name2 типа name1. Все остальные случаи сделать как-то по другому. В частности, запретить заключать объявляемые объекты в круглые скобки, как это возможно в С/С++.


          1. mirrr
            29.10.2015 14:34
            +4

            Еще упрощает задачу программисту в поиске мест объявления переменных, всего-то var и :=


            1. NeoCode
              29.10.2015 15:09

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


        1. Aleksi
          29.10.2015 23:01

          Вот тут подробно написано: blog.golang.org/gos-declaration-syntax


    1. NeoCode
      29.10.2015 10:32
      +5

      Опрератор := сам по себе очень неплох (я восхитился лаконичности и красоте, а также тем как удачно вернули к жизни это старое доброе паскалевское сочетание символов), но реализация ИМХО не совсем правильная. Это оператор, а не замена инструкции объявления, поэтому у меня большие сомнения в выражениях вида a, b := foo(). Неясен статус запятой — это оператор или что? Какой у нее приоритет по отношению к оператору создания объекта? Сейчас как-то не модно стало точное описание всех синтаксических элементов языка, а жаль. Просто показывают кучку примеров — смотрите как просто писать программы — тяп ляп и готово! Вместо того чтобы дать исчерпывающий перечень всех групп синтаксических элементов, их смысл, состав и правила использования.
      У него есть еще одна странность — он не работает внутри выражений. То есть x := (y:=a+b)*c не прокатит, а жаль, это было бы действительно по-хакерски.


  1. Daniro_San
    29.10.2015 09:18
    -9

    Кому то очень нравится Go?
    Или я в чем то не прав?
    Ответь мне, о ты, минусовавший!


  1. kuznetsovin
    29.10.2015 09:32
    -2

    Но понимаете ли, стоит убрать внутренний контекст (фигурные скобки) и код заработает ровно так, как мы ожидаем («after 42»). Интересно девки пляшут, не так ли?


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

    def test():
        a=0
        print "before: {}".format(a) #0
        def up():
            a = 1
            print "insert: {}".format(a) #1
        up()
        print "after: {}".format(a) #0
    

    Так что ничего не очевидного я тут не вижу


    1. namespace
      29.10.2015 10:16
      +2

      В твоем коде очевидно, что `a` — это новая переменная. В коде из топика используется :=, слева от которого новая переменная err. К тому же там контекст может быть еще слабее, чем вложенная функция — цикл или условие. Короче говоря, как-то не очень на руку «читабельному» языку.


      1. VolCh
        29.10.2015 13:36
        +4

        Как минимум для джаваскриптеров не очевидно.


        1. MuLLtiQ
          29.10.2015 19:44

          В JavaScript все проще, там если var или (что еще лучше) let — значит новая переменная, если нет — берем из внешней области видимости. Ну т.е.

          function f() {
            var a = 0;
            var b = 1;
          
            function g() {
              var a = 2;
              b = 3;
            }
          
            g();
          
            console.log(a);  // 0
            console.log(b);  // 3
          }
          
          


          Кстати, лично для меня, по сравнению с тем же Питоном, это гораздо удобнее.


    1. andrewnester
      29.10.2015 10:19
      +3

      вместо def up напишите if
      а вообще тут дело в := и это то как он ведет себя согласно документации
      другое дело, что программист, пишущий/читающий код, может ожидать от этого оператора немного другого


      1. VolCh
        29.10.2015 13:38
        +2

        Главное, что может легко спутать с = чисто визуально. Может точно знать особенности := и = в Гоу, но просто спутать.


  1. semenyakinVS
    29.10.2015 10:15
    +3

    Автор-переводчик, что вы наделали! В статье творится просто невероятная понжовщина, и это ещё плюсовики серьёзно не ворвались.

    А статья любопытная. Из неё понял, что у Go есть определённое родство с Питоном (те же слайсы и механизм наследования в чём-то похожий).
    Как убеждённый фанат плюсов, я, вроде бы, должен радоваться кривулькам конкурентов. Но скажу что думаю: Go просто относительно молодой язык, который ещё успеет отшлифоваться в процессе различных процессов.


    1. namespace
      29.10.2015 10:31
      +10

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

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


      1. semenyakinVS
        30.10.2015 18:46

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


        Если так, то печально. Неужели на Go уже есть достаточное количество написанного кода, чтобы говорить об откатанности его фич?

        а он вообще считает, что подстветка кода не нужна, а лучший редактор это acme


        М-да… А чем подсветка-то им не угодила?


        1. namespace
          30.10.2015 18:52
          -2

          Неужели на Go уже есть достаточное количество написанного кода, чтобы говорить об откатанности его фич?

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

          А чем подсветка-то им не угодила?

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


    1. mickvav
      29.10.2015 22:43
      +1

      Ну, если только на слайсы смотреть, то они ещё в фортране были. ;) Причём синтаксис там в этом месте куда прямее — www.mathcs.emory.edu/~cheung/Courses/561/Syllabus/6-Fortran/array4.html


    1. 0xd34df00d
      30.10.2015 17:30

      это ещё плюсовики серьёзно не ворвались

      А что тут врываться-то.


      1. semenyakinVS
        30.10.2015 18:57
        -1

        Ну, холливара по поводу «С++ vs Go» не развернулся в достаточной степени… И слава Богу!

        П.С.: Хотя в статье-ответке на эту статью по поводу плюсов всё-таки развернулся срач. Вообще, нужно сделать мега-статью, в которой было бы максимально объективное сравнение языков по основным метрикам: скорость работы, лаконичность кода, выразительность (хотя выразительность это спорно), и.т.д.


        1. tyomitch
          31.10.2015 00:58

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


          1. semenyakinVS
            31.10.2015 01:06

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


  1. faost
    29.10.2015 10:57

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


  1. Find_the_truth
    29.10.2015 11:02

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


    1. NeoCode
      29.10.2015 11:17
      +2

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


      1. misha_shar53
        30.10.2015 11:19
        -3

        Я тоже пытаюсь написать свой язык программирования. Если можете вышлите спецификацию своего языка и комментарии на misha_shar53@mail.ru. Хотелось бы ознакомиться.


    1. 6opoDuJIo
      29.10.2015 12:03
      +8

      К тому, что Go интенсивно форсят, почём зря.


      1. Suvitruf
        29.10.2015 13:48
        +1

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


        1. 6opoDuJIo
          29.10.2015 15:27
          -1

          Из-за того, что его так форсят, постоянно появляется куча новых статей и обсуждений

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


          1. Suvitruf
            29.10.2015 23:35
            +1

            Я разве говорил, что «он станет лучше»? Нет. Из комментариев к статье, к примеру, много интересных вещей узнал, даже не связанных с Go.


    1. VolCh
      29.10.2015 13:53

      Есть те, кто к нему (и конкурентам) присматривается для решения в будущем каких-то задач и ещё окончательное решение не принял.


  1. webmasterx
    29.10.2015 11:06
    +3

    а какая подобные операции из примера 1 выглядели бы в Rust?


    1. dbelka
      29.10.2015 11:30
      +15

      Как-то так:

      fn main() {
          let mut numbers = vec![1, 2, 3, 4, 5];
          
          println!("{:?}", numbers);
          println!("{:?}", &numbers[2..]);
          println!("{:?}", &numbers[1..3]);
          println!("{:?}", &numbers[0..numbers.len()-1]);
          
          numbers.push(6);
          println!("{:?}", numbers);
          
          numbers.remove(2);
          println!("{:?}", numbers);
          
          numbers.insert(2, 3);
          println!("{:?}", numbers);
          
          let copiedNumbers = numbers.clone();
          println!("{:?}", copiedNumbers);
      }
      


      1. namespace
        29.10.2015 11:38
        +22

        >Какие дураки Rust придумывали… Другое дело Пайк и Томпсон, они-то знают, что вставка в непрерывный кусок памяти это сложная задача!


      1. zagayevskiy
        29.10.2015 12:00

        Какой «громкий» язык. Зачем везде этот восклицательный знак?


        1. dbelka
          29.10.2015 12:09
          +11

          «!» вместо мата :) Если серьезно, так обозначаются макросы в Rust, мощная возможность для расширения языка и сокращения шаблонного кода. Вот тут можно почитать kgv.github.io/rust_book_ru/src/macros.html


      1. Googolplex
        29.10.2015 22:40
        +4

        Хочу ещё добавить, что разделение «массивов» на owned vectors, которые, собственно, являются владельцами куска памяти, и срезов, которые просто предоставляют окно в уже существующий кусок памяти (естественным образом вытекающее из концепций владения и заимствования), на мой взгляд, удобнее и понятнее в работе, чем модель, когда сами срезы владеют памятью, на которую указывают.


  1. NeoCode
    29.10.2015 11:34

    Еще интересные мысли по слайсам. В D применяется специальный оператор $, который означает размер того массива, в контексте которого он применяется. То есть arr[$-1] это как раз последний элемент массива. Похоже чем-то на отрицательные индексы Питона, но пожалуй более строго… ведь индекс может получиться и в результате вычисления, вызова функции, и кто знает какой он там получится?


    1. namespace
      29.10.2015 11:40
      +2

      Звучит как очень правильное решение, которое можно спарировать фанбою Go—адепту «читабельности». Но ты не волнуйся, Ваня все равно выкатит что-то вроде того, что $ это большое зло и вводить его в язык это гигантская трагедия, лучше заниматься циркоплясками с длиной контейнера. А вообще я его в PHP видел этот знак, нам это точно не надо!


      1. VolCh
        29.10.2015 13:54

        А ещё он в баше активно используется. Ещё перл на ум приходит.


        1. NeoCode
          29.10.2015 14:01

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


        1. 0xd34df00d
          30.10.2015 17:33
          +1

          И в хаскеле, да.

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


    1. ik62
      29.10.2015 15:16

      Более того, его можно оверлоадить dlang.org/operatoroverloading.html#dollar


  1. bolk
    29.10.2015 11:44
    +3

    > Мне особенно нравится это шапито с троеточиями…
    Троеточие это вот: ? (triple colon), «…» — многоточие.


    1. defuz
      29.10.2015 12:37
      +7

      И не многоточие, а три точки (в исходном коде Go).
      </занудство>


  1. nikitadanilov
    29.10.2015 12:13

    > Да-да, явное лучше чем неявное, но все же?

    Наверное проблема в том, что использование []FancyInt как []Stringy некорректно?
    Иначе в Join можно было бы сделать

    items[0] = new FancyRune;
    

    У массивов нет нетривиального subtyping-а про типу элемента, это вроде как общеизвестно.


  1. Daniro_San
    29.10.2015 12:15
    -16

    Мне нравится это бурное обсуждение.
    Впрочем, как и все обсуждения Go.
    Хомячки (и не только на картинках) бесятся.
    Неплохо расслабляет после работы с C# кодом.


  1. ZOXEXIVO
    29.10.2015 12:16
    +16

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


  1. trong
    29.10.2015 13:24
    +5

    Складывается впечатление, что автор статьи не вполне понимает, что при всех достоинствах и недостатках Go — это все же нишевый язык. У компании в какой-то момент остро встал вопрос — где брать высококласных разработчиков в требуемых количествах? Пока тебе требуется 1-2-5 — это сложно, но реализуемо. Если тебе требуется тысяча — это практически неразрешимая задача. В итоге Google пошел альтернативным путем — разработал язык на которой можно быстро (пере)обучить человека с минимальным бэкграундом и при этом не дать возможности этому новообученному специалисту простора для выстреливания в ногу.

    Если вы рассчитываете на проект, который будет писаться в течении 10-15 лет, то вы должны понимать, что команда может смениться и даже не раз и вам нужно, чтобы код был прост и понятен, чтобы в нем не было немыслимых заковырок, определяемых не реальной потребностью реализовать именно так, а скорее мотивацией «зырьте как я могу».

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


    1. defuz
      29.10.2015 13:35
      +1

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


      1. trong
        29.10.2015 13:46
        +2

        Ответом на этот вопрос лично для меня является другой вопрос:
        Хочешь ли ты быть крут за счет того, что умеешь на питоне извернуться так, что 90% других разработчиков этот код просто не поймут или ты хочешь быть крут потому, что разрабатываешь большой и клевый продукт которым будут пользоваться тысячи? Язык разработки — это большая, но все же часть продукта, который на выходе может быть плох или хорош, и определяется это не только тем, насколько удобно работать со слайсами.

        consul, fleet, etcd — вот лишь малый список достойных вещей написанных на Go.


        1. Suvitruf
          29.10.2015 13:51

          Странно, что вы Kubernetes не назвали или Docker )


          1. trong
            29.10.2015 13:52
            +1

            Ну что первым вспомнилось :)


        1. defuz
          29.10.2015 14:16
          +3

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

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


      1. Alex_At_Net
        29.10.2015 13:52
        -1

        Я хочу :-)


    1. Yuuri
      29.10.2015 16:29
      +1

      разработал язык на которой можно быстро (пере)обучить человека с минимальным бэкграундом и при этом не дать возможности этому новообученному специалисту простора для выстреливания в ногу.

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

      Кажется, когда-то так появилась Java?


  1. neolink
    29.10.2015 14:17
    +3

    по пунктам:
    1) по сути отсылка к отсутствию generics
    2) может момент не самый очевидный для данного кода, но если понимать, что такое интерфейс (а это ссылка на что-то) то все логично
    3) вы объявили новую переменную, то есть в момент написания этого кода вы допустили ошибку, это не проблема языка он сделал ровно то, что вы от него хотели.
    4) есть подозрение, что не от хорошего кода желание такие касты делать.
    5) что тут не очевидного, даже синтаксически, вы объявляете новые переменные, почему они должны быть другого типа чем у вас есть в массиве?

    после этого вас уже откровенно начинает нести:
    > Окей, но как только дело доходит до «читабельности», Роб Пайк решает, что надо ВНЕЗАПНО добавить запятые.

    6) это синтаксис, в import и var нет в том месте запятых, а конструкции:
    numbers := []int{
    3, 5
    4
    }
    явно выглядят куда хуже, кстати запятая в последнем элементе обязательна и проверяется при компиляции, так что пункт вообще не в кассу
    7) кодогенерация запускается вручную, явно, отдельной командой.
    то есть вместо того чтобы писать https://github.com/golang/net/blob/master/html/atom/gen.go можно сразу описать конструкцию которую можно запустить и сгенерировать новый файл table.go

    В общем не сказать, что проблем нет, например если бы добавили опцию при которой можно отключить unused import стало бы чуть лучше, с generics кодогенерация спасает, но для стандартных контейнеров например это не очень классно хранить в каждом месте копию кода, а в реализации на интерфейсах нужно постоянно делать type assertion.
    Плохо ещё, что аннотации есть только в структурах, а для типа например их нет.

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


  1. tgz
    29.10.2015 16:03
    +7

    Как хорошо, что божественный rust…


    1. Aleksi
      29.10.2015 22:56
      -4

      … не используется в production. ;)


      1. defuz
        29.10.2015 23:34
        +2

        почему?


        1. Aleksi
          30.10.2015 00:00

          Все известные мне люди, которым нравится Rust, и которых я спрашивал про production, говорили «нет». У меня даже появилось ощущение, что Rust красив только до тех пор, пока на нём не начинаешь писать что-то настоящее.

          Но, конечно, это анекдот, в обоих смыслах.


          1. defuz
            30.10.2015 00:25
            +5

            Я думаю ваши друзья отвечали «нет» в первую очередь потому, что еще некоторое время назад (до выхода версии 1.0) язык очень активно изменялся: обычная практика, когда правильный код не компилировался потому что у вас компилятор недельной давности (такого периода было достаточно, чтобы компилатор можно было считать устаревшим). Также, сразу после выхода 1.0, много важных вещей в стандартной библиотеке еще не было стабилизировано. Это добавляло ощущения нестабильности.

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


            1. Gorthauer87
              30.10.2015 11:15
              +1

              Библиотеки часто пока не так хороши, как сам язык, вроде батареек и много, но они еще сыроваты. Да и пока нет ничего вменяемого для UI. А язык, вроде бы, должен подходить для программирования UI.
              Ну и в bare metal разработке тоже пока все сыро, хотя и выглядит крайне вкусно.

              А еще пока нет нормальных средств интернационализации.


          1. Alesh
            30.10.2015 01:40
            +2

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

            И да, он действительно красив)


  1. Stronix
    29.10.2015 19:01
    +1

    // Чтобы скопировать слайс, ты должен будешь написать это:
    //
    copiedNumbers := make([]int, len(numbers))
    copy(copiedNumbers, numbers)

    copiedNumbers := numbers[:]


    1. JIghtuse
      29.10.2015 19:18
      +1

      В Python работает так же, к слову.


    1. neolink
      29.10.2015 22:24
      +5

      не совсем (вернее это совсем не копирование): http://play.golang.org/p/b2hrKq5rem
      об этом даже прямо в блоге написано: http://blog.golang.org/go-slices-usage-and-internals
      > Slicing does not copy the slice's data. It creates a new slice value that points to the original array


      1. JIghtuse
        30.10.2015 06:08

        Ого, а это неожиданно. То есть, единственный способ — это указанный в статье?


        1. Stronix
          30.10.2015 12:57

          Вот ещё вариант

          numbers := []int{1,2,3,4,5}
          copiedNumbers := append([]int{}, numbers...)
          


          1. neolink
            30.10.2015 13:01

            ну да, только надо глянуть есть там аллокация на пустой массив или этот момент оптимизирован


      1. Stronix
        30.10.2015 11:56
        +1

        Хм, да, вы правы.


  1. Stronix
    29.10.2015 19:11
    +5

    // Интересный факт: отрицательные индексы не работают!

    The omission of this feature (present in Python, for example) is deliberate

    When performing arithmetic on slice indices it would be unfortunate if an erroneous negative result «just worked» as a reverse index. This leads to subtle bugs.

    There are readability benefits to the status quo, also. It is clear that the Go expression s[:i] is creating a slice of s that is i bytes long. If i could be negative then the reader would need more context to understand the slice expression.

    This is in keeping with Go's general philosophy of avoiding subtle syntactic tricks.


    1. 0xd34df00d
      30.10.2015 17:39

      When performing arithmetic on slice indices it would be unfortunate if an erroneous negative result «just worked» as a reverse index. This leads to subtle bugs.


      Эм, а какие выражения допустимы в контексте индексов слайса?


  1. Stronix
    29.10.2015 19:20
    +1

    Про range вообще не понял, там же ясно стоит :=


  1. Stronix
    29.10.2015 19:47

    Причина №2. Нулевые интерфейсы не всегда нулевые :)

    golang.org/doc/faq#nil_error


    1. mayorovp
      29.10.2015 20:16
      +2

      Да, это объясняет почему так получается — но не объясняет зачем так сделано.


      1. Aleksi
        29.10.2015 22:54

        Это частный случай Zero value. Во всех остальных случаях оно очень удобно и эффективно.


  1. dmbreaker
    29.10.2015 21:50
    +7

    Складывается ощущение, что автор не очень хорошо знает Go и имеет не так много опыта в разработке вообще. Но это ладно.
    1) То что описано в реальном коде приходилось использовать от силы один раз. А частые операции со слайсами в Go куда понятнее и лаконичнее. В общем, если автору частно приходиться писать такой код, то может и не в языке дело.
    «numbers[:-1]» — ну это просто Python головного мозга. Зачем требовать фич одного языка у другого, если другой намеренно хочет избежать опасных конструкций.
    2) nil interface. Пункт с которым я соглашусь. Считаю это багом, который почему-то не хотят исправлять.
    3) Ну сокрытие, ничего забавного — все очевидно. Если ты хороший разработчик — это не проблема. Причем одно забавное сокрытие в Go таки есть, но автор про него не знает, оно связано с именованными возвращаемыми значениями. Впрочем статические анализаторы на это обычно ругаются.
    4) К сожалению передать []struct как []interface передать нельзя, было бы чуть удобнее, но откуда истерика по этому поводу?
    5) foreach. Если вдумчиво читать стандарт языка, то все становится очевидным. Приводить в пример все остальные языки не имеет смысла, т.к. во многих из них нет понятия указателя. В данном случае это критично. Автор приводит в пример C++. Давайте смотреть ретроспективно — сколько прошло времени, чтобы в C++ вообще появился foreach?
    6) unused imports — это наоборот то, что в Go прекрасно. Сначала раздражает строгость, но спустя некоторое время привыкаешь и испытываешь удовольствие от чистоты кода, в котором нет лишних неиспользуемых переменных и импортов. Это и полезно — по первым строчкам становится ясно что использует файл, а что нет. И авторы заморачивались не только на простоту, как думает автор, но и на скорость компиляции, лишние импорты при этом совершенно ни к чему. И многие конструкции в языке строги, подозреваю, отчасти и по этой причине.
    7) Про кодогенерацию ничего не скажу, т.к. не приходилось использовать.

    Итого, смотрим на эти пункты. Кто, откровенно может сказать, что эти пункты вообще что-то говорят про плохой дизайн языка?
    Да, есть недочеты, но в C++ их куда больше, причем это не такие безобидные вещи — там с легкостью можно отстрелить себе обе ноги носовым платком.
    Везде есть свои особенности и недостатки — идеала нет. Но, с моей точки зрения и опыта, это один из языков с лучшим дизайном, при этом минималистичным. Это не значит, что Go можно изучить за 2 недели. Нет, за 2 недели на нем можно научиться писать и если на этом остановиться, то будут появляться такие статьи. Чтобы изучить Go нужно хотя бы месяца 4 активной разработки и параллельного изучения особенностей и конструкций языка. Сладкий фрукт, да не каждый до него достает.


  1. Stronix
    29.10.2015 21:56
    +1

    написать красивую функцию insert()

    Возвращаясь в полусонном бреду, без всяких проверок… play.golang.org/p/22eNJO0Js3


    1. nwalker
      29.10.2015 22:17
      +1

      Код, в котором есть interface{} не имеет права называться красивым.


    1. mirrr
      30.10.2015 09:08
      -1

      Я уже таких выше наплодил) Им нужно с проверками типов во время компиляции)


  1. blackstrip
    29.10.2015 22:40
    -6

    Наконец-то пост ненависти про Go. А то все предыдущие статьи — один пиар и хомяк этот кривой на логотипах. Слишком форсят свой суперязычок. Хочется к его названию подписать «вно».


    1. semenyakinVS
      29.10.2015 23:50
      +8

      Интересы: «Assembler, Delphi, Интерфейсы». Конечно, у Delphi более ясное будущее чем у Go.

      Хабраслив или тонкая игра в провокацию?


      1. blackstrip
        30.10.2015 13:50
        -9

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


        1. semenyakinVS
          30.10.2015 14:04
          +1

          А новомодное дмецо, которое по хорошему на одну ладонь положить, другой прихлопнуть и растереть — лезет сегодня из всех дыр


          Д-р, простите, не узнал вас в гриме.

          image

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

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


    1. Daniro_San
      30.10.2015 12:05
      -3

      Ох уж эти масоны.
      Хотят поработить мир с помощью Go


  1. KIVagant
    30.10.2015 17:27
    +1

    > Причина №1. Манипуляции со слайсами просто отвратительны
    Согласен

    > Причина №2. Нулевые интерфейсы не всегда нулевые :)
    Согласен, что это нелогично

    > Причина №3. Забавное сокрытие переменных
    Вообще не понял, почему автор ожидал 42 в последней строке. По-моему, поведение Go в этом случае верное.

    > Причина №4. Ты не можешь передать []struct как []interface
    Для начала хочется сказать, что тип rune для int32 вообще вызывает странное ощущение. Почему было не оставить просто int32, не понимаю?
    А с самой причиной согласен в том контексте, что в Go (ну прям как в до боли знакомом php) нужно помнить про очередные подводные камни. Со временем к ним так привыкаешь, что даже и не воспринимаешь как проблему. Типа «да это же все знают!».
    И да, если вспомнить про реализацию чего-то, подобного DI, с ожидаемыми параметрами с типами определенных интерфейсов, то причина выглядит серьёзной.

    > Причина №5. Неочевидные циклы «по значению»
    Ну смотря для кого неочевидные. Как по мне, отсутствие явного указателя передачи по ссылке уже говорит, что будет возвращено только значение.

    > Причина №6. Сомнительная строгость компилятора
    Согласен полностью, во время отладки бесит всё время комментировать и раскомментировать подключаемые библиотеки для отладки.

    > Причина №7. Кодогенерация в Go это просто костыль
    Согласен. Вообще против концепции генерации кода. Если язык не в состоянии предоставить удобные инструменты из коробки или с помощью фреймворков, то что-то в нём не так.

    P.S.:
    От себя добавлю ещё вот этот кейс, с которым я столкнулся при попытке написать первую простенькую программку на Golang, и который был сразу же заминусован на SO.


    1. mayorovp
      30.10.2015 19:11

      Когда уже перестанут называть рейтинг -1 словом «заминусован»?..


      1. KIVagant
        30.10.2015 19:14

        Да, я не успел это отредактировать, вы наблюдательны. (Кстати, он был -2, хех)


    1. divan0
      31.10.2015 19:12

      Для начала хочется сказать, что тип rune для int32 вообще вызывает странное ощущение.

      rune — это специальный тип данных для работы с Unicode. Формально это алиас на int32, но выделен в отдельный тип, так как представляет конкретную сущность — «букву», или же codepoint.

      Вообще против концепции генерации кода.

      habrahabr.ru/post/269887


      1. KIVagant
        31.10.2015 22:39
        +2

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


  1. beresovskiy
    31.10.2015 14:11
    +6

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


    1. namespace
      31.10.2015 14:13
      -9

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


      1. beresovskiy
        31.10.2015 14:16
        +3

        Вам решать, но divan0 тогда прав, утверждая, что ваш пост «был выдан за перевод» (http://habrahabr.ru/post/269817/).


        1. namespace
          31.10.2015 14:42
          -4

          Так это и есть перевод, это перевод моей блогозаписи. Фраза «был выдан за перевод» звучит так, как будто я кого-то обманываю!


          1. beresovskiy
            31.10.2015 14:45
            +4

            Я понял, что вы автор оригинала и перевода лишь после того, как я узнал, что tucnak@medium == namespace@habr.


            1. namespace
              31.10.2015 14:54
              -7

              Ничего себе, вы случайно не чувствуете себя обманутым?!