Если вы подумываете начать писать на Go, то вот что вам следует знать

    Ваш любимый питомец пишет на Go и получает больше вас, а вы ещё нет? Не теряйте времени… Такая мысль может родиться у читателя от обилия статей по Go. Некоторым даже компании предлагают переучиться на этот язык. И, если вы хоть раз задумывались освоить язык, то я хочу вас предостеречь. Вернее показать странные вещи, попробовать объяснить зачем они и потом вы уже сами сделаете вывод нужен ли вам Go.

    Го - это портируемый Си


    Для кого эта статья


    Статья в первую очередь предназначена тем людям, для которых важна выразительность языка. И одновременно для тех, кто хочет пощупать Go.
    Я сам Си++/Python разработчик и могу сказать, что это сочетание является один из оптимальнейших для освоения Go. И вот почему:

    • Go очень часто используется для написания backend-сервисов и очень редко для всего остального. Существует ещё две популярные пары для этого же: Java/C# и Python/Ruby. Go, на мой взгляд, нацелен именно на то, чтобы забрать долю у пары Python/Ruby.
    • Go наследует своё странное поведение именно из нюансов синтаксиса Си, неочевидно спрятанных в языке. Поскольку в Go есть чёткие моменты отторжения до такой степени, что порой хочется удалить компилятор Go и забыть, то понимание принципов Си и того, что Go в каком-то смысле является надмножеством Си, позволяет их существенно сгладить.


    Что по-поводу пары Java/C#? Go ей ни разу не конкурент, по крайней мере пока он молод (речь про версию Go 1.11).

    Чего не будет в статье


    • Мы не будем говорить о том, что Go плох, так как в нём нет фичи X, как в языке Y. У каждого языка свои правила игры, свои подходы и свои поклонники. Хотя кого я обманываю, конечно же об этом нам придётся поговорить.
    • Мы не будем сравнивать напрямую интерпретируемые и компилируемые языки.


    А что будет? Только конкретные случаи дискомфорта, которые доставляет язык в работе.

    Начало работы


    Хорошим вводным по языку мануалом является короткая онлайн книга Введение в программирование на Go. Читая которую вы довольно быстро наткнётесь на странные особенности. Приведём для начала первую партию из них:

    Странности компилятора


    Поддерживаются только египетские скобки
    Поддерживаются только египетские скобки, то есть следующий код не компилируется:
    package main
    
    func main()  // Не компилируется
    {
    
    }
    

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

    Многострочные перечисления должны заканчиваться запятой
    a := []string{
    	"q"  // Нет запятой, не компилируется
    }
    

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

    Не использовал переменную? Не компилируется!
    Нет, это не шутка.
    package main
    
    func main() {
    	a := []string{
    		"q",
    	}
    	// Не компилируется, переменная не использована
    }
    


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

    Неиспользуемые параметры приходится заглушать и это смотрится странно, хотя в питоне так тоже можно:
    for _, value := range x {
        total += value
    }
    



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

    «Безопасный» язык


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

    Вот цитата одного из создателей языка:
    «Ключевой момент здесь, что наши программисты (прим.пер.: гуглеры) не исследователи. Они, как правило, весьма молоды, идут к нам после учебы, возможно изучали Java, или C/C++, или Python. Они не в состоянии понять выдающийся язык, но в то же время мы хотим, чтобы они создавали хорошее ПО. Именно поэтому язык должен быть прост для понимания и изучения.»

    Спионерено отсюда: Почему дизайн Go плох для умных программистов.

    Так значит вы говорите безопасный язык?
    var x map[string]int
    x["key"] = 10
    

    и после запуска программы получаем:
    panic: runtime error: assignment to entry in nil map
    


    В этом невинном примере мы «забыли» выделить себе память и получили ошибку времени выполнения. Так а какой безопасности может идти речь, если вы меня не спасли от неверной ручной работы по выделению ресурсов?
    Хабраюзер tyderh замечает, что:
    Безопасность заключается в том, что при выполнении отлавливается ошибка, а не происходит неопределённое поведение, способное произвольным образом изменить ход выполнения программы. Таким образом, подобные ошибки программистов не способны привести к появлению уязвимостей.


    Следующий пример:
      var i32 int32 = 0
      var i64 int64 = 0
      
      if i64 == i32 {
        
      }
    

    Вызовет ошибку компиляции, что как бы нормально. Но поскольку в Go пока (пока!) нет шаблонов, то очень часто они эмулируются через интерфейсы, что может рано или поздно вылиться в такой код:
    package main
    
    import (
    	"fmt"
    )
    
    func eq(val1 interface{}, val2 interface{}) bool {
    	return val1 == val2
    }
    
    func main() {
    	var i32 int32 = 0
    	var i64 int64 = 0
    	var in int = 0
    
    	fmt.Println(eq(i32, i64))
    	fmt.Println(eq(i32, in))
    	fmt.Println(eq(in, i64))
    }
    

    Этот код уже компилируется и работает, но не так как ожидает программист. Все три сравнения выдадут false, ибо сначала сравнивается тип интерфейсов, а он разный. И если в данном случае ошибка явно бросается в глаза, в реальности она может быть сильно размыта.

    powerman поделился ещё один примером ложных ожиданий:
    func returnsError(t bool) error {
    	var p *MyError = nil
    	if t {
    		p = ErrBad
    	}
    	return p // Will always return a non-nil error.
    }
    err := returnsError(false)
    if err != nil {
      # Истина
    }
    

    Интерфейс с nil не равен просто nil, будьте осторожны. В FAQ языка этот момент есть.

    Ну и завершая про безопасность. Разыменование в языке убрано, а вот спецэффекты в зависимости от вида доступа от доступа (по указателю или по копии) остались. Поэтому следующий код:
    package main
    
    import "fmt"
    
    type storage struct {
    	name string
    }
    
    var m map[string]storage
    
    func main() {
    	m = make(map[string]storage)
    	m["pen"] = storage{name: "pen"}
    
    	if data, ok := m["pen"]; ok {
    		data.name = "-deleted-"
    	}
    
    	fmt.Println(m["pen"].name) // Output: pen
    }
    

    Выведет pen. А следующий:
    package main
    
    import "fmt"
    
    type storage struct {
    	name string
    }
    
    var m map[string]*storage
    
    func main() {
    	m = make(map[string]*storage)
    	m["pen"] = &storage{name: "pen"}
    
    	if data, ok := m["pen"]; ok {
    		data.name = "-deleted-"
    	}
    
    	fmt.Println(m["pen"].name) // Output: -deleted-
    }
    

    Выведет "-deleted-", но пожалуйста, не ругайте сильно программистов, когда они на эти грабли наступят, от этого в «безопасном» языке их не спасли.
    В чём же отличие в этих чёртовых кусках?
    В одном примере:
    m = make(map[string]storage)
    а в другом:
    m = make(map[string]*storage)


    Ха, вы думали всё? Я тоже так думал, но неожиданно напоролся ещё на одни грабли:
    Наступить на грабли
    package main
    
    import "fmt"
    
    var globState string = "initial"
    
    func getState() (string, bool) {
    	return "working", true
    }
    
    func ini() {
    	globState, ok := getState()
    	if !ok {
    		fmt.Println(globState)
    	}
    }
    
    func main() {
    	ini()
    	fmt.Println("Current state: ", globState)
    }
    

    Возвращает initial и это верно ибо оператор := создаёт новые локальные переменные. А его мы вынуждены были использовать из-за переменной ok. Опять таки всё верно, но изначально строчка
    globState, ok := getState()
    могла выглядеть как
    globState = getState()

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

    А это значит, что теперь нам надо у PVS просить статический анализатор для языка Go.

    Краткий вывод: безопасность присутствует, но она не абсолютна от всего.


    «Единообразный» язык


    Выше в разделе странности компилятора было указано, что при неверном форматировании кода, компилятор упадёт. Я предположил, что это было сделано для единообразия кода. Посмотрим насколько код единообразный.
    Вот например, два способа выделить память:
    make([]int, 50, 100)
    new([100]int)[0:50]
    

    Ну да, ну да, это просто фишка функции new, которую мало кто использует. Ладно будем считать это не критичным.

    Вот например, два способа создать переменную:
    var i int = 3
    j := 6
    

    Ладно, ладно, var используется реже и в основном для резервирования под именем определённого типа или для глобальных переменных неймспейса.

    Ладно, с натяжкой будем считать Go единообразным языком.

    «Колбасный» код


    А вот ещё частая проблема, конструкция вида:
    result, err := function()
    if err != nil {
        // ...
    }
    

    Это типичный кусок кода на Go, назовём его условно колбасой. Среднестатистический код на Go состоит на половину из таких колбас. При этом первая колбаса сделана так result, err := function(), а все последующие так result, err = function(). И в этом не было бы проблемы, если бы код писался только один раз. Но код — штука живая и постоянно приходиться менять местами колбасы или утаскивать часть колбас в другое место и это вынуждает постоянно менять оператор := на = и наоборот, что напрягает.

    «Компактный» язык


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

    И в первую очередь из-за «колбас», о которых я упоминал чуть выше. Сейчас ноябрь 2018 и все Go программисты ожидают версию 2.0, потому что в нём будет новая обработка ошибок, которая наконец покончит с колбасами в таком количестве. Рекомендую статью по ссылке выше, в ней суть проблемы «колбасного» кода разъяснена наглядно.

    Но новая обработка ошибок не устранит все проблемы компактности. По прежнему будет не хватать конструкций in и not in. На текущий момент проверка нахождения в map значения выглядит так:
    if _, ok := elements["Un"]; ok {
    }
    

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

    Молодой язык и бедный синтаксис


    К Go существует очень много написанного кода. И есть просто потрясающие вещи. Но не редко вы выбираете между очень плохой библиотекой и просто приемлемой. Например SQL JOIN в одном из лучших ORM в GO (gorm) выглядит так:
    db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
    

    А в другом ORM вот так:
    query := models.DB.LeftJoin("roles", "roles.id=user_roles.role_id").
      LeftJoin("users u", "u.id=user_roles.user_id").
      Where(`roles.name like ?`, name).Paginate(page, perpage)
    

    Что ставит пока под сомнение вообще необходимость использовать ORM ибо нормальной поддержки защиты от переименования полей не везде просто нет. И ввиду компилируемой природы языка может и не появиться.

    А вот один из лучших образцов компактного роутинга в вебе:
    a.GET("/users/{name}", func (c buffalo.Context) error {
      return c.Render(200, r.String(c.Param("name")))
    })
    

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

    Спорные недостатки


    Публичные функции


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

    Многоэтажность


    Если вам нужен словарь объектов, то вы напишите что-то такое:
    elements := map[string]map[string]string{
    		"H": map[string]string{
    			"name":  "Hydrogen",
    			"state": "gas",
    		},
            }
    

    Пугающая конструкция, не правда ли? Глазу хочется каких-нибудь скобочек, чтобы не спотыкаться. К счастью они возможны:
    elements := map[string](map[string]string){
            }
    

    Но это всё, что позволит вам форматтер go fmt, который почти наверняка будет использоваться в вашем проекте для переформатирования кода при сохранении. Все остальные вспомогательные пробелы будут выпилены.

    Атомарные структуры


    Их нет. Для синхронизации надо явно использовать мьютексы и каналы. Но «безопасный язык» не будем вам пытаться мешать писать одновременно из разных потоков в стандартные структуры и получать падение программы.
    helgihabr любезно напомнил, что в 1.9 появился sync.Map.

    Тестирование


    Во всех не очень безопасных языках безопасность хорошо реализуется через тестирование с хорошим покрытием. В Go с этим почти всё в порядке, кроме необходимости писать колбасы в тестах:
    if result != 1 {
        t.Fatalf("result is not %v", 1)
        }
    

    Понимая ущербность данного подхода, мы сразу нашли в сети библиотеку, реализующую assert и доработали её до вменяемого состояния. Можно брать и использовать: https://github.com/vizor-games/golang-unittest.

    Теперь тесты выглядят так:
    assert.NotEqual(t, result, 1, "invalid result")
    


    Две конвертации типов


    В языке сущность интерфейса имеет особый статус. Они в том числе часто используются, чтобы заткнуть «бедность» синтаксиса языка. Выше уже был пример с реализацией шаблонов через интерфейсы и неявным вредным спецэффектом, порождённым этим случаем. Вот ещё один пример из этой же серии.
    Для преобразования типов можно использовать обычную конструкцию в Си-стиле:
    string([]byte{'a'})

    Но не пытайтесь применить её к интерфейсам, ибо для них синтаксис другой:
    y.(io.Reader)

    И это довольно долго будет вас путать. Я для себя нашёл следующее правило для запоминания.
    Преобразование слева называется conversion, его корректность проверяется при компиляции и в теории для констант может производится самим компилятором. Такое преобразование аналогично static_cast из Си++.
    Преобразование справа называется type assertion и выполняется при выполнении программы. Аналог dynamic_cast в Си++.

    Исправленные недостатки


    Пакетный менеджер


    vgo одобрен, поддерживается JetBrains GoLand 2018.2, для остальных IDE как временное решение подойдёт команда:
    vgo mod -vendor

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

    Достоинства


    Прочитав статью может возникнуть предположении, что над нами стоит надсмотрщик с плёткой и заставляет писать на Go, исключительно ради наших страданий. Но это не так, в языке есть фишки существенно перевешивающие все вышеописанные недостатки.
    • Единый бинарник — скорее всего весь ваш проект скомпилится в единый бинарник, что очень удобно для упаковки в минималистичный контейнер и отправки на деплой.
    • Нативная сборка — скорее команда go build в корне вашего проекта соберёт этот самый единый бинарник. И вам не потребуется возиться с autotools/Makefile. Это особенно оценят те, кто регулярно возится с ошибками Си компиляторов. Отсутствие заголовочных файлов — дополнительное преимущество, которое ценишь каждый день.
    • Многопоточность из коробки — в языке не просто сделать многопоточность, а очень просто. Настолько просто, что очень часто просто импорт библиотеки в проект и использование какого-либо её примера уже может содержать явно или неявно в себе работу с многопоточностью и при этом в основном проекте ничего от этого не ломается.
    • Простой язык — обратная сторона бедности синтаксиса — возможность освоить язык за 1 день. Даже не за 1 день, а за 1 присест.
    • Быстрый язык — в виду компилируемой природы и ограниченности синтаксиса вам будет сложно выжирать много памяти и процессорного времени в ваших программах.
    • Строгая типизация — очень приятно, когда IDE в любой момент знает тип переменной и переход по коду работает как часы. Это не преимущество именно Go, но в нём оно тоже есть.
    • Защита от расширения структур — ООП в Go эмулируется структурами и методами для структур, но правило такое, что это должно лежать в одном файле. И это очень хорошо в плане анализа чужого кода, в Ruby есть паттерн подмешивания и иногда чёрт ногу сломит.
    • Отложенная деинициализация. Лучше всего иллюстрируется примером:
      package main
      
      import (
          "fmt"
          "os"
          "log"
      )
      
      func main() {
          file, err := os.Open("file.txt")
          if err != nil {
              log.Fatal(err)
          }
          defer file.Close()
      
        b, err := ioutil.ReadAll(file)
        fmt.Print(b)
      }
      
      Благодаря
      defer file.Close()
      мы сразу сообщаем рантайму, что, в независимости от того каким и где будет выход из функции, в конце надо выполнить определённый код. Это сразу частично решает проблему с отсутствием деструкторов и почти полностью решает проблему отсутствующих контекстов (например питоновский with).


    Почему так получилось


    Go выглядит как надмножество Си. Об этом говорит очень многое: и похожесть синтаксиса и понимание того, как это может быть легко преобразовано в Си код. Конечно же горутины, сборка мусора и интерфейсы (а вместе с ним RTTI) нетипичны для Си, но весь остальной код легко конвертируется практически регулярками.
    И вот эта природа, на мой взгляд, и диктует почти все приведённые выше странности.

    Резюме


    • Go отлично подходит для быстрого написания экономных и быстрых микросервисов, при этом для этой работы годятся любые опытные разработчики с других языков. Именно в этом вопросе ему мало равных.
    • Go молод. Как верно было отмечено кем-то из комментаторов: «Идея на 5, реализация на 3». Да, как универсальный язык — на три, а чисто для микросервисов на 4. Плюс язык развивается, в нём вполне можно исправить половину описанных недостатков и он станет существенно лучше.
    • Первый месяц работы вы будете бороться с компилятором. Потом поймёте его характер и борьба пройдёт. Но этот месяц придётся пережить. Половина хейтеров языка месяц не протянули. Это надо чётко понимать.
    • Любителям STL надо сказать, что пока придётся собирать с миру по нитке. Ибо пока доступных контейнера три, не считая встроенных map и array. Остальное придётся эмулировать или искать в сторонних библиотеках.


    Библиотеки для тестов


    • github.com/vizor-games/golang-unittest — нормальные человеческие assert и check для тестов, похоже на питон, вдохновлялось им же. С нормальным выводом строчек, где именно тест повалился.
    • godoc.org/github.com/powerman/check — библиотека догружающая стандартный тестовый интерфейс, привнося в неё полезные методы типа: Equal, Less,…. Поделился powerman.
    • github.com/stretchr/testify — ещё одна хорошая библиотека для тестов. Поделился esata.
    • github.com/onsi/ginkgo — ещё одна хорошая библиотека для тестов. Поделился tyderh.
    • github.com/smartystreets/goconvey — BDD подход к тестированию, выглядит очень непривычно, но может кому пригодится. Поделился negasus.

    Что почитать


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

    Подробнее
    Реклама

    Комментарии 481

      –18
      Ну вот, очередной не разобравшийся в языке пишет статью со своим мнением. «Странности» компиляции связаны с тем что компилятор ставит точку с запятой в конце каждой строки автоматически, кроме открывающих скобок и запятых
        +19
        Какое мне, простому разработчику, дело до сложностей компилятора при работе с промежуточным представлением кода?
          +8
          Это не баг, а полезная фича.
          Элементы списка унифицированы, их можно комментировать, копипастить и т.д. без необходимости следить, есть ли запятая на последнем элементе или нет.
          Это уменьшает кол-во ошибок при разработке софта.
          Так что это очко в пользу Go.
            +1

            В любом случае, это отловится на этапе компиляции, что вроде как соответствует духу Go?
            Зачем тогда нагружать компилятор неожиданными для программиста функциями по дополнению кода?

              0
              ну тут проблема в стиле — «вот в json не нужно запятую после последнего элемента ставить, а в Go нужно, ууууу, неудобно».
              @zubor правильно подметил — запятая после последнего элемента в много строчном объекте требуется специально (даже Russ Cox об этом писал), как раз из-за того, что при удалении и добавлении элементов (и генерации кода) не нужно было постоянно проверять последний ли элемент это или нет.
                0
                Т.е. можно [случайно] удалить последний элемент в списке и никто этого не заметит.
                Или скопипастить лишнее в середину — тоже всё ОК будет: всё с запятыми.
                  0
                  На код-ревью заметят, там «красненьким» подсветится.
                    0

                    Это только в случае удаления последней строки, в середину тоже самое, то есть редкий кейс. Да и правильно — git diff и код ревью вам в помощь.

                      0
                      Редкий не редкий, но зачем убирать такую защиту?
                      А если полагаться на дисциплинированность программистов и повсеместную отличную организацию их работы, то можно наверное много чего придумать, для упрощения работы компилятору.
                        0
                        Это не защита, это источник ошибок. При добавлении элемента, нужно постоянно изменять предыдущею строку, что еще и неудобно.
                          0
                          Ошибка пойманная компилятором — не ошибка.
                          А вот, повторю, удаление последнего элемента списка в Go должно отлавливаться уже на других этапах. Которых может и не быть.
                          А работа с синтаксисом языка у опытного программиста вообще на автомате происходит: думаешь о том что должно быть («функция такая-то», «добавить значение в конец списка» или вообще думаешь как алгоритм выразить в языке), а банальный синтаксис руки сами оформляют. Вот если какие-то хитровывернутые конструкции — там да, надо думать и над синтаксисом.
                          А уж запятую поставить или убрать — вообще не проблема.
                            0
                            Как часто вы случайно удаляли последний элемент в списке?
                            У меня никогда такого не случалось.
                            Ошибка — это то, что вам нужно исправить. Вот с удалением (или добавлением) запятой после редактирования кода я сталкивался — json был невалидный.
                              0
                              Статистику такую конечно не веду, но вполне себе могу представить рабочую ситуацию (т.е. не умозрительно, а из каких-то глубин памяти), что при выделении и копи-пасте захватил лишнего (последнюю строку).
                              И компилятор ругнётся сразу в двух местах: и там откуда забрал лишнее (список остался с висящей в конце запятой) и там куда вставил (лишний, не последний элемент в списке без запятой).
                              А в Go, выходит ни то, ни другое компилятор не заметит.
                                +1
                                Вы на компилятор возлагаете какую-то совсем уж странную функцию: «контроль корректности заполнения данных программистом». К тому же не понятно, каким образом отсутствие запятой в конце списка вам должно помочь.
                                  0
                                  Отвечу на последний вопрос:
                                  Если в конце списка и только в конце списка предполагается запятая, то там откуда я забрал лишнюю (последнюю) строку — в конце списка окажется несанкционированная запятая.
                                  А там куда я вставлю вырезанное — будет 2 строки без запятой.
                                    0
                                    Да это-то я понял. Я не понял, чем именно последняя строка списка так сильно отличается от, допустим, предпоследней, или первой, или второй сверху?

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

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

                                    Чота вы с ветряными мельницами воюете…
                                      0
                                      Да это-то я понял. Я не понял, чем именно последняя строка списка так сильно отличается от, допустим, предпоследней, или первой, или второй сверху?

                                      Черт. Я понял! Нужно для каждой строки свой символ ввести. Тогда уж точно будет очень удобно и компилятор защитит нас от ошибки
                                        +1
                                        Можно еще так — четные строки с запятой, нечетные без.
                                        Получается 100% защита от случайного удаления любой строки.
                                        Как тебе такое, 5oclock?
                                          0
                                          Можно вспомнить, что кроме кодов обнаружения ошибок еще есть избыточное кодирование, которое позволит её исправить! Нужно ставить 3 запятых где она должна быть, и 0 там, где не надо.
                                            0
                                            Блин, сперва прям порадовался гениальности решения, но…

                                            Это же не защитит от случайного удаления ровно двух соседних строк! Что делать???
                                              0
                                              Увеличиваете количество запятых, и всё. Для исправления N ошибок требуется N+2 кодирование. Выбираете такое N, которое больше нравится, и используете соответствующее количество запятых.
                                                0

                                                Короче, надо просто добавить капчу в IDE, когда кто-то пытается удалить последнюю строку.

                                                  0
                                                  «Решите простенькое интегральное уравнение, чтобы подтвердить, что вы действительно хотите удалить эту строку».
                                              0
                                              Я придумал, придумал! Еще лучше придумал!

                                              1. Нужно строки нумеровать (строго по порядку) — тогда мы защитимся не только от удаления строки, но еще и от случайных перемещений строки.
                                              2. Вместо спецсимвола нужно использовать хеш от номера строки + её содержимого! Тогда мы заодно сможем защититься от случайного изменения строки!

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

                                              В теории, мы можем защититься еще и от удаления/правки строки сторонним злоумышленником (если этот самый хеш подписывать личной ЭЦП разработчика).

                                              И от чтения врагами (шифрованием получившегося).

                                              ТакЪ победимЪ!!!
                                              0
                                              Я-то не воюю.
                                              Это создатели Go решили из каких-то своих соображений сделать «не так как у всех» и заодно сломали такую вот «защиту последней строки», убрали наглядное подтверждение завершённости списка для читающего код.

                                              Это как питон со своими отступами.
                                              Удалил отступ у последней строки цикла или if'а и никто этого не заметит: ни компилятор, ни тот кто будет читать/сопровождать программу…
                                                0
                                                убрали наглядное подтверждение завершённости списка для читающего код.


                                                Закрывающая скобка уже недостаточно наглядна?
                                                  0
                                                  Раст по-умолчанию тоже расставляет запятые в конце (хотя форматтер можно настроить, и не является ошибкой). Он перестал быть «быстрым языком, направленным на безопасность»?
                                                    0
                                                    Это создатели Go решили из каких-то своих соображений

                                                    https://github.com/golang/go/issues/23966#issuecomment-377997161


                                                    For example, the hacked-up blog post system I built stores a JSON blob at the top of each file, above the post text, because it was very easy to implement that. But I am sick of needing to leave out the comma after the last key-value pair, because it makes adding a new key-value mean editing the previous one too. This is exactly why we allow trailing commas in Go literals. Those annoyances add up.
                                      0
                                      Это не защита, это ересь какая-то, да еще и грабля впридачу.
                          +3
                          А чего Вы ожидали? Человек разбирался в языке за один присест с книгой Введение в программирование на Go ))
                          Я работаю с Питоном несколько лет, попробовав Go остался доволен. Все те «недостатки» про которые все говорят принял как данность, типа: «А, хорошо — здесь это так работает и устроено так! Запомним!»
                          Почему-то некоторые начинают сравнивать этот язык со своим любимым/используемым…
                          Жаль на работе не дают использовать, мол, специалистов мало на рынке (
                            +3
                            А чего Вы ожидали? Человек разбирался в языке за один присест с книгой Введение в программирование на Go ))

                            С чего вы взяли? Я вообще в основном со спецификацией языка работаю и работаю давно.
                              –1
                              Ну шутка же была, что уж Вы — смайлики в конце намекают на рофл. Извините.
                                0
                                Я вообще в основном со спецификацией языка работаю и работаю давно.

                                Почему же для вас тогда неожиданно то, что в Go всё передаётся по значению?
                                  0
                                  Почему же неожиданным, просто в каких-то кусках кода это легко упустить.
                                  –1
                                  Я вообще в основном со спецификацией языка работаю и работаю давно.


                                  make([]int, 50, 100)
                                  new([100]int)[0:50]
                                  


                                  Вот это, все таки, немного разные вещи. Не то чтобы на выходе вы получали принципиально разный результат… Но констракт слайса длины 50 с капом 100 и создание массива из 100 элементов и после этого взятие слайса из первых 50… Как минимум, в полученом слайсе будет кап 50, и поведение при «растягивании» оного может сильно различаться.
                                    +5
                                    Это пример из документации, там утверждается, что это идентичные конструкции.
                                      0
                                      Перечитал, в этом моменте согласен, был неправ, посыпаю голову пеплом.

                                      Однако некоторые ваши позиции достаточно спорны.
                                      var i int = 3
                                      j := 6
                                      


                                      Вторая строка — просто сокращенная запись с автовыводом типа. При этом достаточно очевидно, что именно при объявлении переменной предпочтителен первый вариант, ввиду явного указания типа и по принципу наименьшего удивления.
                                      Однако для возврата значения из функции мы имеем уже:
                                      var i int = someFuncReturningInt()
                                      

                                      против
                                      i := someFuncReturningInt()
                                      


                                      На этом месте нужность сокращенной записи все еще не очевидна, однако при возврате из функции нескольких значений становится понятно, для чего придумана сокращенная запись:
                                      result, err := some2ResFunc()
                                      // вполне себе разворачивается в:
                                      var result ResultType
                                      var err error
                                      result, err = some2ResFunc()
                                      


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

                                          var i int — способ решения «классической проблемы» char *(*(**foo[][8])())[]

                                          Вариант, собственно, хорош, но при этом он ведет(ввиду возможности множественных возвратов) к:
                                          var res int
                                          var err error
                                          res, err = someFunc()
                                          


                                          А это уже достаточно многословно. И эта проблема решается
                                          res, err := someFunc()


                                          Сложно придумать что-то лучшее…
                                          А для j := 6 оно, собственно, не предназначалось, и не рекомендуется.
                                    0

                                    Вы ж сами написали: "Простой язык — обратная сторона бедности синтаксиса — возможность освоить язык за 1 день. Даже не за 1 день, а за 1 присест."
                                    Что, мягко говоря, вообще не правда. Go — весьма сложный для освоения язык, потому что граблей по нему разбросано неимоверное количество. Даже статьи с подборками писали, типа 50 Shades of Go. Часть граблей имеет какое-то более-менее разумное объяснение, а часть — WAT чистой воды. Но в любом случае, освоить его быстро не получится, т.к. опереться на предыдущий опыт с другими языками не получится.

                                      0
                                      Справедливости ради, Go осваивается не медленнее любого другого enterprise-языка, а в подавляющем количестве случаев быстрее.
                                        0

                                        Слишком сильно зависит от бэкграунда, поэтому я бы сказал, что сравнить в общем случае невозможно. Кому-то быстрее Go получится освоить, кому-то — C#.
                                        Я больше про то, что все эти "за 1 день" — не более, чем маркетинговый булшит.
                                        Что-то начать писать можно на 1-й день на любом языке (особенно если у вас уже есть пяток ЯП в арсенале), но это не значит, что он освоен.

                                          +1
                                          Я про то, что:
                                          а) в Go сильно меньше языковых конструкций в принципе, чем в той же Java или C++ или C#. В нем реально примерно столько синтаксиса, что, с хорошим бэкграундом в других языках, читать и понимать код сможешь часа за 3 (без бэкграунда — за день-два), не натыкаясь на непонятные языковые конструкции. Он не то что прост, он прямо примитивен с точки зрения синтаксиса и системы типов.
                                          б) В «обросших энтерпрайзом» языках есть достаточно крупные фреймворки, большая стандартная библиотека и вот это вот все, без которых нельзя писать на этих языках эффективно. Навскидку, для Java нужны стримы, коллекции (и понимание того, в каких ситуациях какие лучше использовать), да тонна еще всего. В общем, в той же Java между «пониманием синтаксиса» и «умением писать эффективный код», да простят меня Java-программисты, реально пропасть. В Go пропасть существенно поменьше.
                                            0
                                            Вы правы, но только в случае с написанием каких-то своих скриптов.
                                            Пытаясь прочитать достаточно сложные библиотеки, вы будете очень сильно разочарованы в том что получается из-за недостатка абстракций и конструкций.

                                            По типу
                                            var descriptor = getDescriptor<Descriptor1/>(param1, param2)

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

                                            var descriptor Descriptor1
                                            ok := getDescriptor(param1, param2, &descriptor)


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

                                            Те же интерфейсы, которые казалось бы унифицированы и крайне просты…
                                            Смотря в исходники(написанный людьми из гугла) я вижу:
                                                // Implements Something interface
                                                func (o object) doSomething (i Interface{}) {}
                                            
                                                // Implements AnotherThing interface
                                                func (o object) doAnotherThing (i Interface{}) {}
                                            


                                            Да, самоучитель по GO я действительно(уже довольно давно) прошел часа за 3. Но читать его мне до сих пор местами крайне сложно, если имеется какая-то более-менее сложная логика требующая определенного кол-ва абстракций.
                                              0
                                              Мне кажется, проблема в том, что вы сетуете на то, что Go не Java…

                                              var descriptor = getDescriptor<Descriptor1/>(param1, param2)


                                              Выглядит очень похоже на абстрактную фабрику. Что есть неидиоматично для Go.

                                              var descriptor Descriptor1
                                              ok := getDescriptor(param1, param2, &descriptor)


                                              Как минимум, именование функции странное для этого кейса. Что должна, собственно, делать эта функция?

                                              И почему, собственно, не сделать *Descriptor1 ресивером функции?

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


                                              Вот и я говорю: не пытайтесь на Go писать на Java.

                                              Те же интерфейсы, которые казалось бы унифицированы и крайне просты…


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

                                              // Implements Something interface
                                              func (o object) doSomething (i Interface{}) {}

                                              // Implements AnotherThing interface
                                              func (o object) doAnotherThing (i Interface{}) {}


                                              Абсолютно верно и идиоматично. В Go интерфейс — сущность необходимая принимающему объекту, а не типу, его реализующему. В этом и «одна из главных фич».
                                                +1
                                                Java здесь вообще не при чем, это просто generic.
                                                Сделать ресивером не выйдет т.к. эта функция должна, по сути, возвращать объекты различного типа.
                                                Плюс ко всему, это код из библиотеки kubernetes, а не какой-то притянутый за уши пример, где java разработчики пытаются писать на GO.

                                                В интерфейсах я делал акцент на комментариях, которые явно говорят о том, что семантики интерфейсов в GO явно не хватает.
                                                  –1
                                                  Сделать ресивером не выйдет т.к. эта функция должна, по сути, возвращать объекты различного типа.


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

                                                  Плюс ко всему, это код из библиотеки kubernetes, а не какой-то притянутый за уши пример, где java разработчики пытаются писать на GO.


                                                  Вот этот?

                                                  // Implements Something interface
                                                  func (o object) doSomething (i Interface{}) {}
                                                  
                                                  // Implements AnotherThing interface
                                                  func (o object) doAnotherThing (i Interface{}) {}


                                                  Абсолютно нормальный код, так оно и должно быть. Что с ним не так?

                                                  В интерфейсах я делал акцент на комментариях, которые явно говорят о том, что семантики интерфейсов в GO явно не хватает.


                                                  Вы просто ожидаете от интерфейсов в Go соответствия интерфейсам Java или C#. Ну, собственно, зря. Интерфейсы Go — несколько иная сущность. Точнее, формально — то же самое. Предназначение — сильно разное.

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

                                                  Всегда есть trade-off. В некоторых языках (не будем показывать пальцем на Java, C#, C++) абстракции, упрощающие жизнь являются частью языка ценой возможности пиления абстракций, эту жизнь сильно усложняющих. Да и собственно ООП-языки задумывались, исходя из подхода «сначала абстракция». Т.е. предполагают сначала рождение абстракции, а затем уже конкретную реализацию в рамках задуманной абстракции.

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

                                                  Собственно, что есть хорошо, и что плохо — вещь сугубо ситуативная. Несложно понять, что каждый из подходов имеет право на существование в определенных условиях. Просто разный подход к проектированию.
                                                    0
                                                    1. Этот «неидиоматичный» подход решается введением generic'ов. Generic'и вводят во 2й версии Go.
                                                    2. Абсолютно нормальный код, который предполагает написание методов исключительно для реализации интерфейса(исходя из комментариев), что в корне противоречит вашему утверждению про интерфейс исходя из существующей реализации.
                                                      0
                                                      2. Ну когда вам нужно реализовать интерфейс, что бы использовать его в библиотеке, то все ок, да. Часто люди упрощают и передают один и тот же обьект, который реализует несколько интерфейсов.
                                                        0
                                                        Боюсь, дженерики, в том виде, в котором они появятся в языке-который-вы-не-используете, вызовут у вас не меньшее негодование, чем все остальное.

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


                                                        Вы невнимательно читали мое утверждение. Еще раз: в классическом ООП интерфейс — неотъемлемая часть типа (класса). Go не ООП-язык. В Go интерфейс — неотъемлемая часть потребителя. Чуете разницу, или нужны примеры кода?
                                                        0
                                                        Да и собственно ООП-языки задумывались, исходя из подхода «сначала абстракция». Т.е. предполагают сначала рождение абстракции, а затем уже конкретную реализацию в рамках задуманной абстракции.

                                                        Нет, не задумывались, и таковыми не являются.
                                                          0
                                                          Видимо, статья — ответ на очередной виток холивара ООП vs ФП от, без сомнения, очень авторитетного чувака, — должна что-то обосновать в контексте обсуждения процедурного Go vs ООП-языки… Но я не уловил, что конкретно.

                                                          Смотри, на пальцах, разница в подходах:
                                                          1. Предварительно согласимся в терминологии. Есть «сигнатура» — предопределенное описание сущности. Есть «интерфейс» — сигнатура набора методов, обязательных к реализации. Есть «тип» — некая сигнатура сущности, которая может реализовывать интерфейс, а может не реализовывать. Есть «экземпляр типа» — инициализированный объект, соответствующий сигнатуре типа. Есть «потребитель» — некоторая сущность, принимающая на вход тип, ограниченный определенными критериями (в нашем случае интерфейсом).
                                                          2. В классическом ООП реализуемый интерфейс является неотъемлемой и обязательной частью сигнатуры типа. Т.е. вам необходимо явно указать, что тип реализовывает указанный интерфейс.
                                                          3. Также интерфейс является неотъемлемой частью сигнатуры потребителя.
                                                          4. В Go реализуемый интерфейс не является частью сигнатуры типа, и, по большому счёту, не имеет к оной никакого отношения.

                                                          Что это дает.

                                                          В классическом ООП для того, чтобы изменить сигнатуру ресивера (в случае, если вам понадобилось уметь работать с несколькими типами), вам нужно заодно поменять сигнатуру потребляемого типа (что ведет к дополнительной грабле вида «тип пришел к нам из сторонней библиотеки, изменить/унаследовать можно не всегда»). В Go для изменения сигнатуры потребителя нам нужно только изменить сигнатуру потребителя, потребляемый тип остается неизменным.

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

                                                          «Побочными эффектами» можно считать «интерфейсы-из-одного-метода» и некоторые другие вещи, неочевидные и кажущиеся странными «со стороны». Но в целом решение достаточно цельное.

                                                          В «классических» ООП-языках есть инструменты для подхода «сначала абстракция». Есть, заодно, до кучи, целые библиотеки, состоящие исключительно из абстрактных типов/методов, из шаблонов и дженериков. Я не говорю, что это плохо. Я говорю, что в Go от этого решили отказаться — только и всего.
                                                            0
                                                            Это называется тайпклассы, которые при этом по некоторому недоразумению применяются автоматически ко всем типам, которые похожи по сигнатуре.

                                                            Проектирование тайпклассов несколько отличается от проектирования интерфейсов, но все еще не настолько, чтобы это было кардинально иной деательностью.

                                                            В «классических» ООП-языках есть инструменты для подхода «сначала абстракция». Есть, заодно, до кучи, целые библиотеки, состоящие исключительно из абстрактных типов/методов, из шаблонов и дженериков. Я не говорю, что это плохо. Я говорю, что в Go от этого решили отказаться — только и всего.

                                                            Хз, обычно делают так, как чувак из статьи. Нужен класс — пишем класс. Никаких интерфейсов/фабрик/менеджеров. Понадобилось абстрагироваться — окей, берем, вводим соответствующие абстракции. Нет — оставляем классы как они есть, безо всякой шелухи.
                                                              0
                                                              Это называется тайпклассы,


                                                              Ну да, идея прикочевала из C++, куда была занесена ветром из Хаскеля.

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


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

                                                              Хз, обычно делают так, как чувак из статьи. Нужен класс — пишем класс. Никаких интерфейсов/фабрик/менеджеров.


                                                              Вы же понимаете, что:
                                                              а) ваша выборка непрезентативна;
                                                              б) моя выборка тоже непрезентативна;
                                                              в) «обычно делают так» пишут в случает отсутствия пруфов.
                                                                0
                                                                Ну да, идея прикочевала из C++, куда была занесена ветром из Хаскеля.

                                                                Ну, гм, в C++ нет тайпклассов. Можно что-то там накостылять на темплейтах, но это не ветром из хаскеля, это просто так получилось по историческим причинам.


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

                                                                  0
                                                                  Ну, гм, в C++ нет тайпклассов.


                                                                  Ну, видимо, есть, раз один из авторов Go утверждает, что идея заимствована из хаскеля, за основу взята C++-реализация.

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


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

                                                                    Ну раз один из авторов Go утверждает, то конечно.

                                                                +1
                                                                Это называется тайпклассы, которые при этом по некоторому недоразумению применяются автоматически ко всем типам, которые похожи по сигнатуре.

                                                                Эти тайпклассы, которые применяются автоматически — это на самом деле структурная типизация (вместо номинальной, как в Java/C#/не-темплейтном-C++). У неё есть свои преимущества, но есть и свои недостатки.

                                                                  0
                                                                  Я смотрю, вы уже с собой спорить начали. Понимаю, приятно пообщаться с единомышленником.

                                                                  У неё есть свои преимущества, но есть и свои недостатки.


                                                                  Абсолютно логично. И это применимо ко всем видам типизации, например.
                                                                0
                                                                В классическом ООП для того, чтобы изменить сигнатуру ресивера (в случае, если вам понадобилось уметь работать с несколькими типами), вам нужно заодно поменять сигнатуру потребляемого типа (что ведет к дополнительной грабле вида «тип пришел к нам из сторонней библиотеки, изменить/унаследовать можно не всегда»). В Go для изменения сигнатуры потребителя нам нужно только изменить сигнатуру потребителя, потребляемый тип остается неизменным.

                                                                Я ничего не понял про ресиверы и вот это всё. Можете пример привести?

                                                                  0
                                                                  Смотрите, у нас есть, предположим, тип TypeA, который имеет, что естественно, некоторую сигнатуру.

                                                                  Есть некий метод сильно сбоку (вероятно даже в другом модуле, а то и в соседней библиотеке), пуст будет Method1(), который на вход принимает этот тип.

                                                                  В процессе вы рожаете некий TypeB, реализующий функционал, аналогичный TypeA. Тут, я думаю, вы согласны, появление таковой необходимости очень часто бывает обоснованным. И вам нужно, чтобы Method1 на вход мог принимать обе реализации. Что же делать, что же делать?

                                                                  Правильно, вы пилите интерфейс (пусть будет InterfaceAB), обобщающий оба типа, и меняете сигнатуру Method1, который теперь вместо TypeA на вход принимает InterfaceAB.

                                                                  И вот в этом моменте вы обязаны явно указать TypeA, что он реализует InterfaceAB, и то же самое сделать для TypeB. Т.е. список реализованных типом интерфейсов — неотъемлемая часть этого самого типа.

                                                                  В случае структурной типизации исходный тип менят ьне нужно. В некоторых моментах оно может оказаться полезным.

                                                                  Собственно, отсюда и рекомендации по использованию интерфейсов в Go: Go-шный интерфейс предельно короток, Go-шный интерфейс определяется рядом с потребителем типа, Go-шный интерфейс описывает ровно то, что нужно потребителю от потребляемого типа, Go-шных интерфейсов много.
                                                                    0
                                                                    Тут, я думаю, вы согласны, появление таковой необходимости очень часто бывает обоснованным.

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


                                                                    И вот в этом моменте вы обязаны явно указать TypeA, что он реализует InterfaceAB, и то же самое сделать для TypeB. Т.е. список реализованных типом интерфейсов — неотъемлемая часть этого самого типа.

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


                                                                    Трейты в расте работают аналогично, насколько я знаю.

                                            +6
                                            Часто читаю отзывы, что питонистам нравится Go и все не могу понять — почему?
                                            Чем он вас цепляет после Питона?
                                            Я хоть владею Питоном не профессионально, но даже учить Go и его особенности неприятно. Мне куда ближе тот же Kotlin.
                                              0
                                              Дешёвый способ получить хороший перфоманс по сравнению с Python, когда это начинает быть важно.
                                                0
                                                С удовольствием перешёл с питона на го. Как уже сказано в соседнем комментарии — быстродействие приложений заметно выше получается. Потребление памяти заметно ниже.
                                                Интерпретаторы, честно говоря, никогда особо не нравились. Тащить за скриптом чемодан без ручки в виде интерпретатора и библиотек, такое себе развлечение. С го гораздо проще. Ну и по мелочи, потом всё-таки сложилось мнение, что питоновские отступы это не лучшее решение, скобки удобнее. Статическая типизация удобнее. Реализация асинхронности очень понравилась.
                                                В общем, к питону уже не вернусь.
                                                Как раз таки, если оперировать словом «хайп», то оно к питону больше подходит.
                                                Питон тут, питон здесь, питон суют куда только можно. Там где в общем и не очень понятно почему именно питон. Люди, которые даже пары строк написать не умеют и никогда не программировали, есть таких много в айти индустрии, всё равно знают, что питон это круто и модно.
                                                  –4
                                                  Про Go только второй год не затыкаются. До этого были Scala и Ruby. Ну и где они теперь?
                                                  Именно это — хайп. Домашние проектики и десяток стартапов. Go повторит историю Ruby.
                                                  Питону уже нормально так лет, он в топ3, он в матером продакшене.
                                                    +4
                                                    он в матером продакшене

                                                    Это да, гигатонны костылей, одноразовых и райтонли скриптов, всякой внутренней автоматизации…
                                                      0
                                                      > Домашние проектики и десяток стартапов.

                                                      Ничего себе заявления. А вы случайно свои сайтики не в DigitalOcean хостите? А дропбоксом пользуетесь?
                                                      Все проекты в CNCF написаны на Go (а те что не были, как linkerd, переписали на Go). Вот тут у Go как раз своя ниша — все что связано с платформой.

                                                      Как бы вам не хотелось, но Go это как раз не домашние проектики.
                                                    0

                                                    Я пишу на питоне с версии 1.4(примерно с 2003 года), пару лет назад перешёл на go. Так вот основное преимущество go для меня в том, что на нем нельзя так просто выстрелить себе в ногу, как это можно сделать на питоне. Ошибки на этапе компиляции, статическая типизация, потребление памяти, горутины и предсказуемое поведение программы.

                                                      +3
                                                      Я пишу в компаниях, где принято покрывать код тестами, и при таком подходе на выходе, что Python, что Go дают результаты одинакового качества.
                                                        +1
                                                        Например, не совсем правда…
                                                          0

                                                          Зачем тестировать то, что может проверить тайпчекер?

                                                            +2
                                                            Чтобы проверить не поменялась ли логика при рефакторинге.
                                                              0
                                                              А можно пример такой логики, которая может поменяться при рефакторинге?
                                                                +1
                                                                Любое бизнес поведение программы.
                                                                  0

                                                                  Хорошо, давайте с другой стороны. А можно пример такого рефакторинга, который бы сломал бизнес-логику приложения?

                                                                    0
                                                                    У вас метод с большим количеством вложений if и for в друг друга. При очередном фиксе было решено разбить на 4 разных метода вызывающих друг друга. Но не все вызовы были расставлены правильно.
                                                                      0

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


                                                                      Условно, вложенный for пробегает по подмассиву — значит, у соответствующей функции будет тип [a] -> [a], а у вызывающей её — [[a]] -> [[a]]. С if'ами и фильтрацией, скажем, чуть сложнее, но можно аналогично.

                                                                        +1
                                                                        Я дружелюбно вам завидую, но вакансий в моём городе по Haskell нет.
                                                                +1
                                                                Тут фишка в чем:
                                                                — либо вы пишете тесты для проверки логики + тесты на валидность типов входных данных;
                                                                — либо вы пишете тесты для проверки логики, а валидность типизации входных данных за вас делает тайп-чекер.

                                                                Т.е. тайпчекер — это «встроенный в язык тест на валидность типа входных данных».
                                                                  0

                                                                  — либо вы не пишете тесты, а валидность логики тоже проверяет тайпчекер.

                                                                    0
                                                                    Довольно сложно представить как это может работать, но я готов поверить, что в Хаскеле это решается дизайном языка.
                                                                      0

                                                                      Как раз недавно в соседнем треде похожий пример приводил.


                                                                      Положим для простоты, что вы пишете свою хитрую арифметику поверх своего хитрого типа MyInt и хотите проверить, что функции сложения и вычитания корректны. Тогда можно написать функции с типами


                                                                      -- свойства сложения
                                                                      sumZero : (x : MyInt) -> 0 + x = x
                                                                      sumAssoc : (x, y, z : MyInt) -> x + (y + z) = (x + y) + z
                                                                      sumCommutes : (x, y : MyInt) -> x + y = y + x
                                                                      
                                                                      -- свойства вычитания
                                                                      subZero : (x : MyInt) -> x - 0 = x
                                                                      subXX : (x : MyInt) -> x - x = 0
                                                                      
                                                                      -- совместные свойства
                                                                      sumSubId (x, y : MyInt) -> (x + y) - y = x
                                                                      sumSubCommutes1 (x, y, z : MyInt) -> (x + y) - z = (x - z) + y
                                                                      sumSubCommutes2 (x, y, z : MyInt) -> (x + y) - z = x + (y - z)
                                                                      sumSubExpands : (x, y, z : MyInt) -> x - (y + z) = (x - y) - z

                                                                      Эти типы будут утверждениями соответствующих очевидных теорем, а их реализации — доказательствами этих теорем. То есть, доказательствами корректности соответствующей логики. Ну там, Карри-Говард, изоморфизмы всякие, propositions-as-types, все дела.


                                                                      И их даже не надо запускать. Достаточно того, что они лежат рядом с MyInt и тайпчекаются.


                                                                      В хаскеле, кстати, этого сделать нельзя. Его система типов недостаточно мощна (в общем случае, конкретно такое конкретно для сложений-вычитаний в нём выразить таки можно, а вот что-то сложнее уже больно), и хаскель неконсистентен как логика, что означает, что написать тайпчекающееся определение можно и для неверных теорем (ибо полноценного totality checker'а там нет, в частности).

                                                                        +1
                                                                        Учитывая наличие в вашем коде subZero, это должен быть fatality checker.
                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                            0
                                                            Не могу сказать, что много кодил на Python (3), но переход на GO прошел на ура по сл.причинам.
                                                            1. компилируемый = скрость
                                                            2. Горутины и каналы — изящно, красиво и многопоточно.
                                                            3. строгий контроль типов (знаю в питон тоже завезли, но в go оно из коробки был)
                                                            4. работа с переменными (динамическое объявление, автоматическая сборка мусора) очень близка к Python, а не к C (чего очень не хочется: ног я себе уже наотстреливал предостаточно)

                                                            т.е. есть плюсы и есть знакомые. приятные вещи. Посему переход — приятен.
                                                              0
                                                              Golang подходит почти всем, кому, нужен перфоманс в скорости по сравнению со скриптовыми языками, такими как ruby, python, php и даже javascript..., но он не нужен тем кто работает с java, с#…
                                                              Простая причина, это почти либа при которой можно девелопить вещи имея стандартные средства для микросервисов, быстро изучается, тяжело отстрелить себе ногу…
                                                              а синтаксис и питоновцы, хотя самому нравится питон… могу предположить что go еще более строгий и минимум скрывает своих задвигов за сахаром, как это делают другие интерпретируемые языки
                                                                0
                                                                о он не нужен тем кто работает с java, с#…

                                                                в данном случае Go упрощает деплоймент и поддержку приложений.
                                                                  0
                                                                  Каким образом?
                                                                    0
                                                                    Деплой? Очень простым: положил бинарь, и оно работает.
                                                                      0

                                                                      Ну с дотнетом точно так же: сделал docker-compose up и оно работает.

                                                                        0
                                                                        Докер-компоуз — не лучший инструмент для прода.

                                                                        Ну и, в конце концов, вы не разрешили проблему. Вместо .NET-рантайма вы на сервер просто тащите докер. Один фиг на 1 зависимость больше, чем у Go.
                                                                          0
                                                                          Докер-компоуз — не лучший инструмент для прода.

                                                                          Как раз наоборот. Надо же и оркестровать сервисы, и вот этим всем заниматься.

                                                                          Ну и, в конце концов, вы не разрешили проблему. Вместо .NET-рантайма вы на сервер просто тащите докер. Один фиг на 1 зависимость больше, чем у Go.

                                                                          Допустим. Чем это плохо-то?
                                                                            0
                                                                            Как раз наоборот. Надо же и оркестровать сервисы, и вот этим всем заниматься.


                                                                            Еще раз, docker-compose — не лучший инструмент для прода. Он очень плохо ведет себя под нагрузкой, он не умеет live-reload, он не умеет адекватного управления ресурсами и т.д. и т.п. Более того, docker-compose — всего лишь управлялка над голым докером, который очень не рекомендуется выставлять наружу.
                                                                            В конце концов, вам дали божественный k8s, а вы на прод голый докер суете. Стыдно должно быть. Я понимаю, что вы, в конце концов, разработчик, а не админ/девопс. А продом должен таки рулить тот, кто умеет его готовить.

                                                                            Допустим. Чем это плохо-то?


                                                                            Плохого-то ничего. Это просто опровержение вашего высказывания, что .NET-приложение деплоится не сложнее Go-шного. Ну вот говорю: сложнее. Для вашего случая всегда +1 шаг деплоя относительно Go.

                                                                            Заметьте, я не говорю, что это прям усраться проблема. Я просто говорю, что N+1 шаг всегда больше, чем N.
                                                                              0
                                                                              Ну ок, трудно с этим спорить. Но на практике разницы я не вижу, что там, что там жмешь кнопку в тимсити и оно полетело в релиз.
                                                                      0
                                                                      тем что не надо обновлять/патчить тот же JVM на всех серверах, где это ранится. А если докер — то тут надо следить за тем, что используют программисты и обновлять какой-нибудь базовый image.

                                                                      Деплоймент будет гораздо быстрее — компилятор быстрее, плюс не нужно тянуть кучи зависимостей типа docker image с OS, сам docker image будет максимально маленький.
                                                                      И все в этом духе.
                                                                        0
                                                                        тем что не надо обновлять/патчить тот же JVM на всех серверах, где это ранится. А если докер — то тут надо следить за тем, что используют программисты и обновлять какой-нибудь базовый image.

                                                                        Обновлять рантайм — м? В дотнете рантайм не обновляется, если проект сделан под core 2.0, то на нем и запускается всю жизнь, пока лид не решит сделать судьбоносное решение и обновить версию. Что будет сделано путём исправление одного dockerfile. Не вижу, чем должна быть проблема.

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

                                                                        Вот не уверен, что компилятор Go быстрее csc.
                                                                          0
                                                                          Обновлять рантайм — м? В дотнете рантайм не обновляется, если проект сделан под core 2.0, то на нем и запускается всю жизнь, пока лид не решит сделать судьбоносное решение и обновить версию. Что будет сделано путём исправление одного dockerfile. Не вижу, чем должна быть проблема.

                                                                          То есть security upgrades не принято делать в .NET?

                                                                          Вот не уверен, что компилятор Go быстрее csc.

                                                                          Можете протестировать. Но я там еще написал, кроме компилятора.
                                                                            0
                                                                            То есть security upgrades не принято делать в .NET?

                                                                            Видимо, нет.

                                                                            Я вообще не припомню уязвимости в .net, которые нужно было бы закрывать.

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

                                                                            А к остальному у меня нет комментариев.
                                                                              0
                                                                              > Я вообще не припомню уязвимости в .net, которые нужно было бы закрывать.

                                                                              Идеальный .net.
                                                                              Вот java мы недавно апдейтили. У нас просто принято делать апдейты безопасности (даже не критические, критические мы сразу же обновляем) периодически.
                                                                                0

                                                                                Не, всё-таки бывают: https://www.cvedetails.com/product/43007/Microsoft-.net-Core.html?vendor_id=26


                                                                                Не знаю, может они образ дефолтный обновляют… Я лично пользуюсь microsoft/dotnet:2.0-runtime, возможно они просто под этот тег пушат рантайм с исправленными уязвимостями, не готов сказать. Кого-то более мелкого версионирования чем 2.0, 2.1. и т.п., по версиям билда там или закрытым уязвимостям я не встречал.

                                                                                0
                                                                                Я вообще не припомню уязвимости в .net, которые нужно было бы закрывать.


                                                                                Учитывая размер .net… видимо, либо они прошли мимо вас, либо их от нас скрывают.
                                                                        0
                                                                        возьмем этот вопрос с другой стороны. Зачем если можно добиться перфоменса на одном языке… как то в друг использовать другой, надо всех обучить и т.д., в таких случаях перфоменса нет, надо добавить еще одно усложнение…
                                                                          0
                                                                          Зачем если можно добиться перфоменса на одном языке…


                                                                          Не на всех задачах из JVM или .NET можно выжать сравнимый с Go перфоманс. При этом их использование сильно не всегда это рентабельно в сравнении с Go.

                                                                          Всегда есть производительность решения * стоимость использования * стоимость поддержки * скорость разработки. В существенном пласте задач результат оценки по этим 4 критериям для Go выгоднее, поэтому его используют.
                                                                            0
                                                                            встречный вопрос, а нужен ли это перфоменс?
                                                                            если и так все ок, и продукт укладывается в функционал который от него требуют?
                                                                              0
                                                                              Если все и так ок, если продукт укладывается в функционал, который от него требует, и продукт уже работает, и соотношение упомянутых выше 4 факторов приемлемо в текущей ситуации, ни один здравомыслящий человек и не будет инициировать переход на другую экосистему же.

                                                                              Если в текущем проекте на дотнете вас все устраивает, откуда вообще может взяться желание переписать его на Go?

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

                                                                              На чём можно сделать лучше — это выбор «идеального мира». В реальном ищут предпочтительный по соотношению «цена/качество» вариант.
                                                                                0
                                                                                Из того, что я видел, обычно начинают выжимать производительность на самом дотнете, а не инициируют переписывание на что-то другое. В ход идет unsafe, SIMD и вот это всё.
                                                                                  0
                                                                                  Вот и я о том же, что заради одного перфоманса на другой стек не переползают, обычно, ровно до тех пор, пока совокупность проблем не становится неразрешимой внутри локальной экосистемы.
                                                                      0
                                                                      Go и Kotlin — практически диаметрально противоположные языки. Go создан так, чтобы было удобно компилятору. А Kotlin — чтобы было удобно программисту.
                                                                      0
                                                                      только вот со структурами как то странновато сделано в плане наследования.
                                                                      хотя по своему тоже фича
                                                                        0
                                                                        Это самая необременяющая реализация типа ООП над структурами Си. При относительной дешёвости достигается гораздо лучшее чтение кода.
                                                                          0
                                                                          я все равно не до конца понимаю почему такое странное наследование.
                                                                          то есть вроде бы откуда уши растут(методы) понятно, но все же.
                                                                            +1
                                                                            Наследование странное, вероятно, потому, что его нет… Композиция есть, наследования нету.
                                                                              0
                                                                              а почему не сделали просто добавлением полей в потомке?
                                                                              вложенность выглядит несколько противоестественно.
                                                                                0
                                                                                Потому что нет никакого потомка, вероятно. Наследования нет, а значит и потомка нет. Есть встраивание типа, и оно именно то, чем кажется.
                                                                                  0
                                                                                  в некотором роде таки есть.
                                                                                  и в принципе понятно как это работает.
                                                                                  наверное при таком подходе всё даже логично.
                                                                      0
                                                                      ИМХО, достаточно нетривиальное поведение.
                                                                        0

                                                                        Вы так говорите, будто это что-то хорошее
                                                                        :)

                                                                          0
                                                                          Так ведь с Го невозможно не разобраться, он ведь такой простой, что прочитав за день книжку с основными моментами можно прод писать.
                                                                            0
                                                                            Проблема в том, что многие executives действительно в это верят.
                                                                          +4
                                                                          Почему гоферы так радуются единому бинарнику, преподнося его как огромный плюс? (Или почему так не делают любители других компилируемых технологий, вроде C++?) Деплой же обычно автоматизирован, а динамическую линковку когда-то тоже преподносили как решение некоторых проблем. Каков здесь истинный практический смысл?
                                                                            +3
                                                                            1. Потому что Go отъедает рынок у python, php, ruby и пр, где нет единого бинарника.
                                                                            2. Потому что иногда приходится возится с бинарником напрямую, и когда это один файл — это удобно.

                                                                            Да, в проекте, где CI уже настолько автоматизирован, что нет в нём никаких правок, это преимущество утрачивается.
                                                                              0
                                                                              Потому что Go отъедает рынок у python, php, ruby и пр, где нет единого бинарника.

                                                                              Это фантазии любителей Go. Python, PHP, Ruby и так для HighLoad не часто использовались. А писать, к примеру, какую-нибудь админку (внутренний проект) с развесистой бизнес-логикой для 1000 юзеров в месяц на Go — это надо сильно упороться.
                                                                              Go играет на поле Scala, Elixir, Haskell, Rust, C-расширения. Причём первые 3 дают более приятные возможности для программирования.


                                                                              Потому что иногда приходится возится с бинарником напрямую, и когда это один файл — это удобно.

                                                                              Зачем с ним возиться напрямую? Вы по FTP что-ли деплоите, как в старые ламповые времена?

                                                                                0
                                                                                Go играет на поле Scala, Elixir, Haskell, Rust, C-расширения.


                                                                                Го играет, в первую очередь, на поле node.js, и отлично играет, собственно.

                                                                                Причём первые 3 дают более приятные возможности для программирования.


                                                                                Scala… ну, как бы, если вы фанат функционального подхода — да, она лучше Go. Однако императив проще в понимании, поэтому функциональное программирование в массах «не взлетает».
                                                                                  0

                                                                                  Ну, кстати, да. Про Node.js забыл.
                                                                                  А что касается массовости, массам и Go не нужен, потому что для большинства проектов вообще пофиг отвечает у тебя сервер за 100 ms или за 30 ms.

                                                                                    0
                                                                                    И я про то же, Go — убийца Node.JS, и конкретно у нее он нишу и отжимает.

                                                                                    А что касается массовости, массам и Go не нужен, потому что для большинства проектов вообще пофиг отвечает у тебя сервер за 100 ms или за 30 ms.


                                                                                    Ну, если говорить о «массах», тут да. Просто есть сегмент «визитка магазина с месячным оборотом в 100 тысяч рублей» — там Go не нужен, если честно. Там лучше какой-нибудь «готовый» фреймворк, а то и шаблон юзать (и по барабану, на чем оно).

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

                                                                                      Это, кстати, заблуждение — думать что между сайтами-визитками и хайлоадом ничего нет по середине. В моём понимании, большинство — это куча разнообразных проектов с многомиллионными бюджетами, но при этом не предполагающих особых нагрузок. Если посмотреть правде в глаза, то даже средне нагруженных проекты от силы 1% от веб-приложений наберётся (сайты-визитки я в расчёт даже не беру)
                                                                                      Я бы сформулировал, что если не предполагается нагрузка выше 10 rps в среднем за сутки, и хотя бы 50 rps в пиковые часы, то даже смотреть в сторону Go — совершенно нецелесообразно. Понятно, что запросы сильно разные бывают и цифры ориентировочные, но порядок примерно такой.

                                                                                        0
                                                                                        А я и не говорю, что посредине ничего нет. Я говорю про то, что у Go ниша — где-то между 10rps в пике и хайлоадом. И в ней он чувствует себя достаточно уверенно (и дело не только в многопоточности).

                                                                                        Я бы сформулировал, что если не предполагается нагрузка выше 10 rps в среднем за сутки, и хотя бы 50 rps в пиковые часы, то даже смотреть в сторону Go — совершенно нецелесообразно.


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

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

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

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


                                                                                          сильные стороны Go — модель асинхронности

                                                                                          Модель асинхронности Go мне, кстати, не нравится, в Erlang/Elixir гораздо интереснее и, на мой взгляд, удобнее.

                                                                                            0
                                                                                            Возможно. Но тот сегмент, который я выше упомянул, это как раз зачастую автоматизация бизнес-процессов с очень развесистой логикой.


                                                                                            Вот именно потому «микросервисы» и взлетают. Смотри, есть непосредственно фронт (веб-морда или клиентское приложение), есть бэк, который отдает данные этому фронту и принимает от него запросы. И «развесистую бизнес-логику», конечно, можно держать прямо в этом бэке, но делать так не принято. Именно в той вещи, в которую смотрит фронт, что-то тяжелое считать не принято. Для того, чтобы считать что-то тяжелое, есть java-приложения, очереди, мемкеши и прочие относительно автономные вещи, которым самое место за гошным беком. Основная ниша Go — это вот эти инфраструктурные вещи, прокси, если хотите, назначение которых — маршрутизация, аггрегация уже готовых датасетов, некоторые конвертации данных и тому подобное. А для того, что обрабатывает «развесистую бизнес-логику», есть Java (и сотни других инструментов), которая является значительно более «нежным» относительно нагрузки инструментом.

                                                                                            Да, на Java/C# и т.д., да на любом языке «общего назначения» можно и инфраструктурные вещи писать, только делать это чувствительно больнее, чем на Go. И вот там его ниша. При том, что порог вхождения в язык, на самом деле, действительно низкий. Любой мало-мальски адекватный специалист во «взрослом» и «многоцелевом» энтерпрайз-языке может позволить себе изучить Go как дополнительный инструмент для ограниченного круга задач. Вот реально, понять Go проще, чем «вкурить» тот же Java Stream API (а это сильно один ИЗ инструментов из джентльменского набора java-девелопера).

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

                                                                                            А вот в момент, когда часть этого портала нужно будет «отзеркалить» или «смаппить» в виде портала, смотрящего наружу… Или если надо будет отдать простенькое API для запроса состояния какого-то элемента системы… Или если вы дорастете до распределенного решения…

                                                                                            Понимаете, ASP.NET — специалист для того, чтобы тупо проксировать наружу один REST-запрос, скорее всего, тупо поднимет еще один инстанс ASP.NET. Вы же согласны, что полученный оверхед принято называть «конским».

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

                                                                                            Модель асинхронности Go мне, кстати, не нравится, в Erlang/Elixir гораздо интереснее и, на мой взгляд, удобнее.


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

                                                                                            Да и в принципе, вот эта подача вида «язык Go умеет простой деплой и приятную модель асинхронности, а вот простой деплой уже 3 месяца умеет C#, а в эликсире прияная асинхронность». Давайте уже «в лоб», не говорить, что фича А встречается в языке Б, а фича В встречается в языке Г. Собственно, изначально при разработке Go так и сказали «в языке нет ничего усраться-прорывного и уникального, и вы тут не найдете ни одной фичи, которую нельзя бы было встретить в других языках». Давайте покажем другой язык с превосходящей комбинацей фич.
                                                                                    +3
                                                                                    Однако императив проще в понимании, поэтому функциональное программирование в массах «не взлетает».

                                                                                    Не проще. Привычнее, если вы последние 10 лет писали на императивных языках, да, но не проще.

                                                                                      0
                                                                                      Тут вопрос в чем.

                                                                                      Императивный подход (если сильно утрировать) — «запишите последовательность действий по порядку в виде команд языка». Собственно, достаточно доступно любому среднестатистическому человеку без существенных отклонений в развитии.

                                                                                      Функциональный подход — «запишите формулу или постройте примитивную математическую модель происходящего». Для математика, вероятно, нет ощутимой разницы (а может и функциональный подход проще, но чуть-чуть). Только людей с математическим складом ума сильно меньше.
                                                                                        +2
                                                                                        Функциональный подход — «запишите формулу или постройте примитивную математическую модель происходящего»

                                                                                        Совсем нет. Функциональный подход — "опишите конвейер преобразований".
                                                                                        Соответственно, каждый этап самостоятелен и не лезет в детали реализации других этапов, а просто принимает входные данные и выдаёт свой результат.


                                                                                        Только людей с математическим складом ума сильно меньше.

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

                                                                                          0
                                                                                          Функциональный подход — «опишите конвейер преобразований».


                                                                                          Хорошо, пусть так. При этом «опишите последовательность действий» по прежнему выглядит проще.
                                                                                            +2

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

                                                                                              0
                                                                                              Вы потеряли нить разговора.

                                                                                              Началось с «императив проще для понимания». Это вы отрицать перестали и стали упирать на то, что «правильнее».

                                                                                              Правильность — это вещь, вокруг которых можно миллион копий поломать. Проще (в итоге) написать корректный код — да, вероятно, в функциональном подходе. Но проще в понимании — императив.
                                                                                                0

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

                                                                                          +3
                                                                                          Императивный подход (если сильно утрировать) — «запишите последовательность действий по порядку в виде команд языка». Собственно, достаточно доступно любому среднестатистическому человеку без существенных отклонений в развитии.

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


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

                                                                                          Как раз-таки математика у людей с детства, и понять функции совсем нетрудно. У меня знакомый преподает, он никак не может объяснить детям, что значит "присвоить результат функции". Не знакомы они с этим.


                                                                                          Это вам только кажется, что императивщина такая простая. Мои друзья-непрограммисты до сих пор выносятся с записи i = i + 1. И хоть я 100 раз говорил, что равно — это присваивание, а не равно собственно, не понимают. Видимо, отклонения в развитии. Или неправильные шотландцы.


                                                                                          Вот вы сами, вам приятнее руками цикл for писать, или всё же let foo = a.map(|x| x + 1)? Тут ведь страшная формула вместо простого и понятного "создай переменную со значением 0, создай переменную с длиной массива а, создай буфер размера с эту переменную, присвой в первый элемент буфера значение первого элемента массива а, увеличь первую переменную на единицу, проверь, что её значение меньше длины массива а".


                                                                                          Математика была придумана людьми для людей, и основывается на их мышлении (логики). Формальная арифметика это как раз раздел логики, которая является систематизацией обычного человеческого мышления. Императивное программирование — придумана людьми для описания состояния машины, и отражает способ мышления этой самой машины — состояния, регистры, и вот это всё. Считать это более человечным, чем математику, это надо быть уж очень привыкшим к императивщине.

                                                                                            0
                                                                                            Мои друзья-непрограммисты до сих пор выносятся с записи i = i + 1. И хоть я 100 раз говорил, что равно — это присваивание, а не равно собственно, не понимают. Видимо, отклонения в развитии. Или неправильные шотландцы.

                                                                                            Или они просто работают в бестиповом лямбда-исчислении, а там у любой функции есть неподвижная точка, даже у succ.


                                                                                            Не удержался от глупой шутки, не обращайте внимания.

                                                                                              0
                                                                                              Вот вы сами, вам приятнее руками цикл for писать, или всё же let foo = a.map(|x| x + 1)? Тут ведь страшная формула вместо простого и понятного «создай переменную со значением 0, создай переменную с длиной массива а, создай буфер размера с эту переменную, присвой в первый элемент буфера значение первого элемента массива а, увеличь первую переменную на единицу, проверь, что её значение меньше длины массива а».


                                                                                              let foo = a.map(|x| x + 1)? Тут ведь страшная формула вместо простого и понятного


                                                                                              Вот прямо ведь истину глаголете.

                                                                                              Классическая императивщина:
                                                                                              a = 0
                                                                                              while a < 10 {
                                                                                              … do something…
                                                                                              a = a + 1
                                                                                              }

                                                                                              Вы меня простите, конечно, но вот это «let foo = a.map(|x| x + 1)» вообще ни разу не понятнее человеку «со стороны». Считать это простой и очевидной конструкцией — в вас говорит профдеформация.
                                                                                                0

                                                                                                У вас претензии к синтаксису или к семантике?


                                                                                                Кстати, ваш цикл не эквивалентен тому выражению.

                                                                                                  0
                                                                                                  У меня претензии к высказыванию о том, что «let foo = a.map(|x| x + 1)» проще и ближе в понимании неподготовленному человеку, чем «for i := 0; i < 10; i++» или, допустим, «for i < 10».
                                                                                                    +1

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


                                                                                                    foo = map (\x -> x + 1) a

                                                                                                    Против, ну, например,


                                                                                                    List<T> foo;
                                                                                                    for (size_t i = 0; i < a.size(); ++i)
                                                                                                        foo.push_back(a[i] + 1);

                                                                                                    Второе правда проще для неподготовленного человека?


                                                                                                    И я, кстати, ошибся, когда писал второе, написал a вместо a[i]. Да, компилятор бы поймал сразу, но всё же.


                                                                                                    Есть ещё, кстати, такая вещь, как выражение намерения. Когда я вижу map, мне сразу ясно, что там поэлементное отображение некоторого контейнера. Когда я вижу foldr, я сразу понимаю, что это правая свёртка. Вижу maximum — поиск максимума. Вижу all — проверка, что все элементы удовлетворяют предикату. Вижу find — поиск элемента по предикату. Вижу filter — ну, сами понимаете.


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


                                                                                                    И примерно поэтому в плюсах, например, есть std::accumulate, std::find_if, std::transform, в C++11 добавили std::any_of/std::all_of/std::none_of (чтобы проверять предикаты), и так далее. И ряд людей (некоторые из которых про всю эту функциональщину и не слышали толком) рекомендует пользоваться этими примитивами вместо того, чтобы каждый раз фигачить цикл. Наверное, это тоже на что-то намекает.

                                                                                                      0
                                                                                                      Меня эти вертикальные палки тоже поначалу удивили, поэтому я с вашего позволения немного перепишу:

                                                                                                      foo = map (\x -> x + 1) a

                                                                                                      Против, ну, например,

                                                                                                      List foo;
                                                                                                      for (size_t i = 0; i < a.size(); ++i)
                                                                                                      foo.push_back(a + 1);

                                                                                                      Второе правда проще для неподготовленного человека?

                                                                                                      Давайте пример из Go тогда приведем:

                                                                                                      var foo []TypeName
                                                                                                      for _, item := range a {
                                                                                                          foo = append(foo, item)
                                                                                                      }
                                                                                                      


                                                                                                      Блин, ну честно, в самом деле:

                                                                                                      map (\x -> x + 1) a версус range a )))

                                                                                                      Понимаете, map, foldr, all, find, filter — это все-таки конструкции, которые могут быть очень лишними в некоторых контекстах. Да и порядок исполнения не всегда очевиден.

                                                                                                        +2
                                                                                                        map (\x -> x + 1) a версус range a )))

                                                                                                        Версус _, item := range a тогда уж.


                                                                                                        Почему там :=, а чуть ниже =? Что вообще значит foo []TypeName? Я такого указания типизированного списка вот ещё вообще ни в одном языке не видел.


                                                                                                        Что за append? Мой мозг вот стриггерился на то, что вы присваиваете всему foo целиком, например.


                                                                                                        Понимаете, map, foldr, all, find, filter — это все-таки конструкции, которые могут быть очень лишними в некоторых контекстах.

                                                                                                        Ну так когда они лишние, ими пользоваться не надо.


                                                                                                        Да и порядок исполнения не всегда очевиден.

                                                                                                        Зачем вам порядок исполнения в map? Кстати, тоже важная компонента описания намерения — map чистый, порядок неважен, можно хоть параллельно элементы вычислять. Важен порядок свёртки? Ну так поэтому есть foldr и foldl по отдельности.


                                                                                                        А вот когда мне в плюсах надо обойти контейнер с конца в начало и сделать это циклом (например, потому, что контейнер не даёт никаких итераторов, и я не могу переписать код в терминах reverse iterator'ов), я начинаю немного нервничать и по 5 раз перепроверять граничные условия, корректность индексации и всё такое.

                                                                                                          0
                                                                                                          Почему там :=, а чуть ниже =?


                                                                                                          Потому что := — сокращенная запись от var имяПеременной ТипПеременной.

                                                                                                          чуть ниже =


                                                                                                          Потому что чуть ниже переменная не объявляется.

                                                                                                          Что вообще значит foo []TypeName?


                                                                                                          var имяПеременной []ТипЭлементаСлайса. В чем, собственно, проблема?

                                                                                                          Что за append? Мой мозг вот стриггерился на то, что вы присваиваете всему foo целиком, например.


                                                                                                          Все верно делаем, местечковая фича.

                                                                                                          А вот когда мне в плюсах надо обойти контейнер с конца в начало и сделать это циклом (например, потому, что контейнер не даёт никаких итераторов, и я не могу переписать код в терминах reverse iterator'ов), я начинаю немного нервничать и по 5 раз перепроверять граничные условия, корректность индексации и всё такое.


                                                                                                          Вот поэтому в Go и нет богатого выбора контейнеров.
                                                                                                            +1

                                                                                                            Да я не сомневаюсь, что на эти вопросы есть ответы. Просто человеку со стороны они, ну, скажем так, неочевидны.


                                                                                                            Я не зря не писал foo = (\x -> x + 1) <$> a, чтобы не было вопросов о том, что такое <$>.


                                                                                                            Вот поэтому в Go и нет богатого выбора контейнеров.

                                                                                                            Контейнер — это и какая-нибудь матрица из сишного мира, в том числе.

                                                                                                              0
                                                                                                              Контейнер — это и какая-нибудь матрица из сишного мира, в том числе.


                                                                                                              В обсуждении выше под контейнерами подразумевались сущности, как минимум, предоставляющие итераторы.
                                                                                                  0
                                                                                                  Вы меня простите, конечно, но вот это «let foo = a.map(|x| x + 1)» вообще ни разу не понятнее человеку «со стороны». Считать это простой и очевидной конструкцией — в вас говорит профдеформация.

                                                                                                  Нет, эквивалентом будет


                                                                                                  int[] result = new int[a.length];
                                                                                                  for (int i = 0; i < result.length; i++)
                                                                                                  {
                                                                                                     result[i] = a[i] + 1;
                                                                                                  }

                                                                                                  Да, это намного менее понятно. Если вам не нравятся вертикальные палки, можно взять JS: let foo = a.map(x => x + 1).

                                                                                                  0
                                                                                                  Как раз императивщина проще для людей только потому…
                                                                                                  На выходе получаем то что пишем, всякие '=' легко запоминаются, справа получает значение что вычисления слева… это легче объяснить и уложить в голове. А вот что есть функции высшего порядка, которые могут получить функцию на входе, вернуть функцию на выходе, потом передать эту функцию куда-то еще и т.д. сложно объяснять.

                                                                                                  императивщина проще объясняется также по другой причине, человек понимает сразу как работает эта шайтан машина, что куда положить и т.д.

                                                                                                  а вот ООП и ФП, как раз любому не подготовленному человеку воспринимается очень тяжело, так как уже надо думать не в духе что вижу, то и получаю, а надо думать совершенно другими абстракциями и в голове еще удерживать что там эта шайтан машина делает…
                                                                                                    0

                                                                                                    Помню натыкался на статьи, где на западе в универах давали ФП первым языком (вроде схему, но это не точно), вместо устовяшихся C/Pascal/Java/Python, и результаты ничуть не хуже. Могу поискать пруф, если нужно, но пока могу сказать, что это чисто вопрос привычки.


                                                                                                    Как только первым языком людям будут давать на жабу, а тот же хаскель, то и результаты будут иными.

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

                                                                                                      Я это пишу, так как самому приходится частенько объяснять людям далеким от программирования, как накалякать код на коленке.

                                                                                                      С программистами все проще, хотя людям из универа которым вдалбливали ООП и они им упаровались, иногда очень сложно объяснить каррирование и калбеке, с ходу они тяжело читают такой код.
                                                                                                      А вот тем кто упаровался ФП, очень тяжело объяснить подходы ООП.

                                                                                                      Сужу по своей практике.
                                                                                                        0
                                                                                                        С программистами все проще, хотя людям из универа которым вдалбливали ООП и они им упаровались, иногда очень сложно объяснить каррирование и калбеке, с ходу они тяжело читают такой код.
                                                                                                        А вот тем кто упаровался ФП, очень тяжело объяснить подходы ООП.

                                                                                                        С этим не могу спорить. Собственно, я с того и начал: синдром утенка. Если бы императивщину так не совали навязчиво, такого бы и не было. Думаю, ситуация будет меняться ближайшие 30 лет. Собственно, она уже изменилась настолько, что в насквозь императивные С++ просовывают std::variant и прочие ФП штуковины.

                                                                                                          0

                                                                                                          Ну, в плюсы просовывают variant потому, что в бусте оно было уже лет 20, наверное.

                                                                                                            0
                                                                                                            Я к тому, что паттерн-матчинг это дефолтная фича, а во всех мейнстрим языках оно только появляется или появилось. В том же C# только в 7 версии пару лет назад. А он ведь еще считается достаточно активно изменяемым.
                                                                                                              +1

                                                                                                              А паттерн-матчинга всё равно нет-то в плюсах! Вот эта вот ерунда на лямбдах — это не паттерн-матчинг хотя бы потому, что внутрь вложенного типа вы не заглянете.


                                                                                                              Есть наркомания на макросах, но её переносом в стандарт и не пахнет.


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

                                                                                                                0
                                                                                                                Я говорил про шаг в нужную сторону, не про реализацию.

                                                                                                                Так-то ждем. Но в шарпах например, появился, а это уже точно мейнстрим.
                                                                                                                  0

                                                                                                                  В шарпе слабенький паттерн-матчинг, но хоть какой-то добавили — уже плюс, тут спору нет.

                                                                                                            0
                                                                                                            я вот сам больше упаровался по ООП, в функциональном программировании пока не силен. Хотя после 1 попытки изучения Хаскеля… ну что я могу сказать, я в восторге, но пока я склоняюсь к мнению Дядюшки Боба, то есть, они могут сосуществовать вместе.
                                                                                                              0
                                                                                                              Конечно. Кстати, неплохая ссылка в тему, спорная, но полезная: github.com/cblp/funlangs
                                                                                                                0

                                                                                                                Странная табличка.


                                                                                                                Ну, в хаскеле завтипы уже, считайте, есть. Просто очень уродливые.


                                                                                                                Что за параметрические модули в идрисе, я не понял.


                                                                                                                Гардов для паттерн-матчинга не указано, а в идрисе без них очень больно. Есть with-matching, но это костыль для этих целей.


                                                                                                                Universe polymorphism в хаскеле выражается через kind polymorphism, если я правильно понимаю.

                                                                                                                  0
                                                                                                                  Спасибо за ссылку.

                                                                                                                  Что касается golang, то его синтаксис, его подход далек от простоты, все же это язык для программистов. Но именно для программистов он прост как 2 рубля.

                                                                                                                  От сюда наверное и хайп вокруг него, особенно когда нужно разрабатывать проекты с большой скорость, в своей нише, да еще и с программистами далекими от 7-10 лет стажа(цифры примерно на бум). Лично этим он меня и подкупает, просто изучить, просто объяснить, ну и бизнес вроде страдать не должен с его скоростью развития, хотя в проде еще не использовал.
                                                                                                                    0

                                                                                                                    Божечки… И сюда таблицу Сыроветского притащили.

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

                                                                                                            Ага, только это иллюзия понимания… Потом человек попадает на сайд-эффект и "весело" проводит неделю за отладкой в полном непонимании как работает эта шайтан машина.

                                                                                                              0
                                                                                                              Я сейчас говорю о людях для которых программирование != профессия, не более. Императивщину понимают в разы быстрее.
                                                                                                                +1

                                                                                                                Это просто ваше мнение или есть какие-то подтверждения, кроме "Очевидно"?

                                                                                                                  0
                                                                                                                  Наверное мое мнение и своя личная статистика, не более.
                                                                                                                  0

                                                                                                                  А вдруг затык в том, что Вы непонятно объясняете функциональщину? xD

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

                                                                                                                    Хотя те вещи которые я объясняю они на мой взгляд читабелнее.
                                                                                                                    Но факт остается фактом… Моя не очень сильная компетенция в данном вопросе может родить непонимание.
                                                                                                                      0

                                                                                                                      Так вещи — это конкретные конструкции. А начинать надо с идей и предпосылок.
                                                                                                                      Например, мне не встречалось людей, у которых возникали проблемы с пониманием Linux pipes, а это по сути реализация одной из основных идей ФП. Хотя тут можно возразить, что не так много людей знакомы с Linux pipes, но концептуально это всё равно достаточно просто.
                                                                                                                      Имхо, проблема ФП в том, что многие авторы книг и статей перебарщивают с математической терминологией, чем создают иллюзию, что всё ппц как сложно.

                                                                                                                        0
                                                                                                                        Тут скорее проблема в практике, то есть думать по разному сложно, человек ищет одну сторону мышления, а потом применяет еще дополнительные если у него есть такая возможность, для этого и нужна практика.

                                                                                                                        Допустим нам надо решить проблему 2+2. Мы можем думать в формате ООП и скормить объекту x,y конструктору, но можем передать аргументы в функцию, а можем просто из функции без параметров вернуть 4.

                                                                                                                        Как правильно поступить?
                                                                                                                        В одном из методов нам нужно создать класс отвечающий за инкапсуляцию и т.д…
                                                                                                                        В другом чистую функцию…
                                                                                                                        В дгугом просто забить и выполнить задание.

                                                                                                                          0
                                                                                                                          думать по разному сложно, человек ищет одну сторону мышления

                                                                                                                          Это довольно спорный тезис. Как же разносторонность мышления?


                                                                                                                          Допустим нам надо решить проблему 2+2.

                                                                                                                          Плохой пример. В данном случае, нет смысла даже в функции, это просто константа.


                                                                                                                          В дгугом просто забить и выполнить задание.

                                                                                                                          Честно говоря, я уже потерял вашу мысль… Изначально Вы писали, что "императивное программирование" интуитивно понятнее. Доминирующий вариант императивного программирования — ООП. Но теперь Вы, похоже, уже согласны, что ООП для человека без спец.подготовки — это сложно.

                                                                                                                            0
                                                                                                                            Пример не удачный, как наверное и любой пример без контекста.

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

                                                                                                                            Воспринимать это как объект(ООП), как процесс(ФП), как просто должное и не городить абстракции вокруг задачи(императивное...)?

                                                                                                                            Это довольно спорный тезис. Как же разносторонность мышления?


                                                                                                                            разносторонность субъективная вещь на мой взгяд, есть опыт, а он как раз субъективен.

                                                                                                                            Ради интереса возьмем размышления такого адепта как Егор Бугоенко и его отношением к ООП, но ведь во многих вопросах он прав, но чисто в технических нет(от сюда и так много споров и негодований), какое решение мы применим, скорее всего такое которое нам ближе)
                                                                                                                              0
                                                                                                                              Воспринимать это как объект(ООП), как процесс(ФП), как просто должное и не городить абстракции вокруг задачи(императивное...)?


                                                                                                                              Маленькая поправочка, не «императивное», а процедурное. ООП-языки в подавляющем количестве случаев императивны.
                                                                                                                                0
                                                                                                                                ООП языки во многом дают возможность решать проблемы императивно, даны инструменты… я к тому за что топит Егор Бугаенко в поисках идеальных ОО языков программирования. Но мыслить как ООП программист и как он будет делать другой вопрос.
                                                                                                                                Хотя я убеждаюсь в том что лучше мешать ООП и ФП, пока мое субъективное мнение.
                                                                                                                      0
                                                                                                                      Но с другой стороны, легко объяснить не процесс формирования цифр, передачи параметров, а скорее проще объяснить состояние инкаплсулированного стейта в объекте, при всем при этом, легко в голове почти у каждого среднестатистического человека укладывается ограниченный контекст, особенно если предметная область еще не близка.
                                                                                                                        0
                                                                                                                        проще объяснить состояние инкаплсулированного стейта в объекте, при всем при этом, легко в голове почти у каждого среднестатистического человека укладывается ограниченный контекст

                                                                                                                        Вы рассматриваете какие-то очень тривиальные примеры, оторванные от реальной практики. Спору нет, ООП даёт быструю иллюзию понимания. А осознание того, что оно ложно чуть менее, чем полностью, приходит только с годами опыта.
                                                                                                                        Попробуйте какие-нибудь простенькие паттерны типа абстрактной фабрики или моста объяснить человеку со стороны.

                                                                                                                          0
                                                                                                                          Ну так ООП и ФП направлены… не верно пишу и говорю, все это направлено на упрощение, на легкую поддерживаемость, не все практику ООП и ФП могут дать легкость понимания кода. Но все мы боремся со сложностью…

                                                                                                                          Извините за такой абстрактный ответ
                                                                                                                        0
                                                                                                                        но тут мне почему-то вспоминается DDD, а не парадигма.
                                                                                                                      +1
                                                                                                                      Потом человек попадает на сайд-эффект и «весело» проводит неделю за отладкой в полном непонимании как работает эта шайтан машина.


                                                                                                                      Есть другая крайность. Человек на чисто-функциональном языке весело проводит две недели, силясь понять, как этого самого сайд-эффекта добиться…
                                                                                                                        +1

                                                                                                                        Это вы по опыту говорите или, ну, так?


                                                                                                                        В ФП с эффектами и их разделением всё очень хорошо.

                                                                                                                          0

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

                                                                                                          +4

                                                                                                          Я радуюсь прежде всего простоте всей системы сборки в целом. В своё время знатно наплевался, когда пытался просто собрать (иногда модифицировать) в системе сборки опенсорсных проектов на C/C++. Помимо прочего, там еще и жуткий зоопарк.


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


                                                                                                          Короче, тут не особо важно, один бинарник или нет, важно, что сборка не вызывает такого количества боли.

                                                                                                            +5
                                                                                                            Скачайте бинарную софтину для линукса (буквально вчера скачал anydesk, а запустить не смог), а потом поищите отсутствующие библиотеки, тогда будет понятно преимущество статических бинарников.
                                                                                                              +1
                                                                                                              С этим у меня обычно справляется пакетный менеджер)
                                                                                                                +5
                                                                                                                А потом, когда у Вас _ВСЁ_ запускаемое будет статическим Вы будете лихорадочно бегать по магазинам скупая оперативную память? Статическая линковка хороша до поры до времени или в спецслучаях.