Pull to refresh

Comments 64

Golang это "си для кластеров", но рафинированный, чтобы поменьше стрелять себе в ногу -- это понятно, средний уровень разработчика теперь пониже, зато серверов и программистов у гугла много. Си был довольно простым и ограниченным языком для написания одностраничных утилит под стиль unix, Го это тоже самое, только юникс теперь распределенный

Кажется вы не до конца понимаете значение слова тоталитарный) Go не заставляет и не запрещает, он просто дает набор возможностей. Возможно набор небогатый, но достаточный)

Более того, насколько я понимаю, он особо не ограничивает фантазию. Кто-то может работать с ошибками через panic() или глобальную last_error, синхронизировать goroutines через классические мьютексы и эмулировать наследование используя generics, интерфейсы и embedded structs (хотя учитывая то как хейтят сейчас ООП, думаю таких найдется немного)

Go как раз заставляет, запрещает и дистимулирует

Можно ли работать с ошибками через panic? Можно, но это очень не удобно, потому что в std и сторонних библиотеках принято использовать error, получится смешение подходов, которые еще нужно как-то соединять

Можно ли сихронизировать горутины через классические мьютексы? Да, но зачем, если есть sync.Mutex?

Можно ли эмулировать наследование через embedding? И да и нет, полноценного наследования все-равно не получится. Методы и поля через embedding невозможно переопределить так

Возможно набор небогатый, но достаточный)

Согласен, в этом и суть! Есть все, что нужно для бекенд разработки, ни больше ни меньше. В этом и суть "тоталитарности", дать разрабу достаточно функциональности, чтобы он мог эффективно писать код, а также чтобы он не изобретал велосипед, при этом не слишком много фичей, чтобы он не было зоопарка реализаций

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

Это не когда, «неудобно», «некрасиво» или «не нужно», а когда запрещено и жестко наказуемо. И не собственным чувством прекрасного, а государством или близким к нему по степени власти над вами органом.

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

В богатой доменной модели Go плетётся в хвосте.

Rust > C#>Java>PHP>Go

Rust - sum types (enum), pattern matching, трейты, ownership - отлично ложится на domain modeling в духе Влашина ("Making Illegal States Unrepresentable").

C# подтянулся: records, discriminated unions пока нет нативно. Java - sealed classes, но многословно. PHP - слабая типизация мешает.

Go в хвосте: нет enum с данными, нет дженериков до недавнего (и они слабые), error handling через if err != nil - домен тонет в бойлерплейте. Structs + interfaces - плоская модель, сложные инварианты выражать тяжело.

Но Go и не для этого. Его ниша (ой, тут утверждали про тотальность/универсальность?) - сетевые сервисы с простой доменной логикой, где anemic model + процедурный стиль нормально работает. Проблема когда на Go пытаются писать сложный домен - получается месиво.

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

Это с чем столкнулся
Ну и ещё десяток недостатков...

Согласен, собственно поэтому Go и используют только в бекенде и инфраструктуре бекенда, а в геймдеве, фронте, мобилке, системной разработке и тд его почти никогда не используют

О основной фишки голэнга - тоталитарности, что ограничивает возможности разработчика

Я не писал о том, что Го надо использовать везде и ко всему или что его дизайн самый лучший, я писал о том, что сам Го уникален не горутинами, а ограничениями

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

Го создан не для разработчика, а для себя, любимой корпорации:

  1. Функциональность только для нужной себе области применения

  2. Замена Явы, чтобы не зависеть от кокурента

  3. Лёгкая замена дешёвых разработчиков на галерах

Почему в Go не как не завезут оператор '?' как в Rust? Не было бы такого бойлерплейта

f, err := os.Create(filename)
    if err != nil {
        return err
    }

было бы

f := os.Create(filename)?

Много чего нет. Вот более полный паттерн, мой любимый

Обработка ошибок не загрязняет бизнес логику. Функция read_port - две строки чистой логики, оба ? невидимо делают конвертацию через From. Вся механика ошибок вынесена в определения типов, которые живут отдельно.

В Go та же функция - половина тела это if err != nil { return ... }. Обработка ошибок перемешана с логикой 1:1

Это то что Скотт Влашин называет "railway oriented programming" - happy path читается линейно, а ошибочный путь идет параллельно и не мешает

Аналог в веб: main() - контроллер, read_port - сервис

use std::fs;
use std::num::ParseIntError;
use thiserror::Error;

#[derive(Debug, Error)]
enum ConfigError {
    #[error("failed to read config")]
    Io(#[from] std::io::Error),

    #[error("failed to parse port")]
    Parse(#[from] ParseIntError),
}

fn read_port(path: &str) -> Result<u16, ConfigError> {
    let content = fs::read_to_string(path)?;  // IoError -> ConfigError::Io автоматически
    let port = content.trim().parse::<u16>()?; // ParseIntError -> ConfigError::Parse автоматически
    Ok(port)
}

fn main() {
    match read_port("/etc/app.port") {
        Ok(port) => println!("port: {port}"),
        Err(e) => eprintln!("Error: {e}"),
    }
}

И аналог

package main

import (
    "errors"
    "fmt"
    "os"
    "strconv"
)

type ConfigError struct {
    Op  string
    Err error
}

func (e *ConfigError) Error() string {
    return fmt.Sprintf("%s: %v", e.Op, e.Err)
}

func (e *ConfigError) Unwrap() error {
    return e.Err
}

func readPort(path string) (uint16, error) {
    content, err := os.ReadFile(path)
    if err != nil {
        return 0, &ConfigError{Op: "failed to read config", Err: err}
    }
    port, err := strconv.ParseUint(string(content), 10, 16)
    if err != nil {
        return 0, &ConfigError{Op: "failed to parse port", Err: err}
    }
    return uint16(port), nil
}

func main() {
    port, err := readPort("/etc/app.port")
    if err != nil {
        var ce *ConfigError
        if errors.As(err, &ce) {
            fmt.Fprintf(os.Stderr, "Config error: %v\n", ce)
        }
        return
    }
    fmt.Printf("port: %d\n", port)
}

Эргономика - главное отличие. В Rust компилятор заставляет обработать каждый вариант ошибки (match должен быть исчерпывающим). Добавил вариант в enum - компилятор покажет все места где не обработал. В Go ошибка это error интерфейс, забыл проверить конкретный тип - узнаешь в проде

Но в бедной бизнес логике - в мире го - это не существенно

Если сервис - CRUD с минимальной логикой, типичный микросервис на 5 эндпоинтов, то Go-шный бойлерплейт терпим, а Rust-овые типы избыточны. Проблемы начнутся при росте сложности (рост числа вариантов ошибок, числа слоёв)

И мусор в логах го. То есть искать не по типам ошибок (Rust, C#это элементарно), а по конкретным сообщениями. Как там ELF настраивать в таких условиях даже не знаю.

Мрак

Но никто так же делает. Просто сервисы долго не поддерживаются, переписывают полностью вместе с ELF настройками.

Свистоперделки пожалуйста, только не пишите на го долгоживущий код.

Вы спросите, как же пишут долгоживущие проекты вроде кубера? Всё плохо: там полно кастомных слоёв-костылей поверх языка.

В частности костыль для обработки ошибок, в кубере свои типизированные ошибки через k8s.io/apimachinery/pkg/api/errors

type StatusError struct {
    ErrStatus metav1.Status
}
// metav1.Status содержит Reason, Code, Message

Так что из-за таких сложностей написание кубер на Go сложнее чем если бы он был на Rust. С использованием LLM, конечно) это сильно снижает порог входа в Rust

В обработке ошибок паритет - глаз привычно пролистывает простыни if err в Го, а в расте меньше строк но надо приглядываться к знаку вопроса в конце

Так что из-за таких сложностей написание кубер на Go сложнее чем если бы он был на Rust.

А это уже смена темы и передергивание. От того что у Го не самая удачная обработка ошибок, недостатков раста это не перекрывает.

"если бы" - ну так напишите.

Зачем искать ошибки по типам, если можно по стектрейсу, он разве не удобнее?
Собственно крайне много проектов, тот же k8s и cockroachDB используют ошибки с стактрейсом и все ок

Это их официальная позиция

TL;DR: даже в Гугле не смогли придти к единому решению, как лучше обрабатывать ошибки; языку уже 15 лет, уже поздно такое делать; так будет два способа делать одно и то же; неудобно дебажить (нельзя воткнуть принт в `if err != nil`)

Но разве в Java нет горутин?

Нет

Как можно сравнивать Go и Java ? Java это тормозное корпоративное болото в котором никто не считает никакие ресурсы...ни машинные, ни человеческие. Go программа будет работать на 90% target машин сразу,а Java вы сначала замучаетесь искать нужную версию,а потом все бросите и пойдете переустанавливать новую систему потому что нужной версии java нет даже для не старой версии системы. Вот как раз недавно имел занимательное развлечение с UniFi network server который вдруг решили обновить Java 17 на Java 25, только эти "эффективные" программисты забыли что рекомендуемая ими Debian 12 Bookworm (вышедшая всего 2 года назад) НЕ ИМЕЕТ Java25 headless. Т.е. теперь все клиенты должны или обновить систему до Debian 13 или поставить гигабайты X11+ вместе с полной версией Java25 , чтобы просто работал простенький web server. Думаю что если бы они писали на Go, то все уместилось бы в десяток мегабайт и работало бы на системах 20 летней давности. Вот поэтому люди и выбирают Go, а не потому что в Java тоже НЕТ горутин.

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

Любить наследование необязательно, а вот уметь использовать, где оно есть - надо : )

А может быть это с вами что то не так раз вам проще 100 раз сделать Copy-Paste вместо наследования? Я люблю наследование, более того...бывает что в языках, отличных от С++ мне не хватает множественно наследования. И у меня никогда небыло проблем с поддержкой проектов с множественным наследованием. Если же язык не поддерживает классы и наследования, то все равно их концепция может быть реализована, просто будет намного более не красиво и сложнее в поддержке, но вы все равно тем или иным способом будете это использовать потому что альтернатива это copy-paste

У наследования есть прекрасная не copy-paste альтернатива - композиция :)

Вы наверное не понимаете что такое композиция и чем она отличается от наследования.Композиция работает когда вы хотите использовать готовый функционал готового класса и ему не нужен доступ к вашему классу потому что он уже полностью готов и самодостаточен. Вы не можете переопределить ничего при композиции не меняя интерфейсам взаимодействия с классом, который используете при композиции. А наследование подразумевает что родительский класс имеет полный доступ к наследнику и вы можете менять логику родительского класса БЕЗ изменения интерфейса взаимодействия. Возмем простой пример. У вас есть класс который реализует список (list) чисел вы ходите сделать класс который его расширяет и делает его упорядоченным списком. При наследовании я просто наследуюсь от него и переопредяляю метод добавления чтобы он вставлял не в конец, а в нужное место согласно нужному порядку....все, переопределил 1 метод и готово. А теперь сделайте тоже самое композицией чтобы я мог передать любой из этих классов одному методу. В лучшем случае для этого надо будет определить интерфейс, потом сделать класс заготовки списка с доступом ко всем его элементам, и только после этого вы сможет включить этот класс в композицию финальных классов где уже реализуете интерфейс используя copy-paste для обоих классов. И при каждом изменении интерфейса вы должны менять все где он упоминается и реализован. И что будет проще поддерживать и использовать?

Много букв, мало знаний.

Есть яп, для которых композиция неявно переписывается как вызов метода и наоборот.

Включая даже мультиметоды. Там же список яп

Мало букв, ноль знаний. Сразу видно "господина" теоретика, который не написал ни одной реальной строчки кода в своей жизни. В мире есть много чего (кроме конечно ваших знаний, вот чего нет так уж нет...даже больше нескольких предложений воды не осилили). Вы можете извращатся как хотите, это ваше дело как эмулировать наследование, но проще и лучше от этого не становится ни код ни его поддержка. Для начала автор хотябы должен разъяснить чем по его мнению композиция лучше наследования и как он использует эту "альтернативу" в конкретном примере. Ну от ответить лучше с аргументами, а не ссылками на wiki про мультиметоды пытаясь сойти за умного. Ум проявляется в делах, а не ссылках на чужие слова. Давайте пример как вы обходите наследование композицией с переписыванимем методов в месте со статьей как вы потом будете это поддерживать, если конечно вас раньше не сожгут на костре за такое издевательство. А потом и сравним что проще и кому промыли что то. Только делайте это на языке который поддерживает и наследование и композицию, а дальше хоть мультиметодами, хоть корягами, главное чтобы проще, нагляднее и понятнее чем наследование. Вот мы тут посмеемся.

Учебник по расту или ним вам в руки.

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

Мда, конкретика и аргументы достойны несостоявшегося программиста. Неужели все так плохо и такой теоретик не может даже аргумент сформулировать, раз уж код не получается. Вот вам ссылка что такое аргумент https://ru.wikipedia.org/wiki/Аргумент_(логика)

Я привёл конкретные языки, в которых есть расширенная диспетчеризация вызовов без классического ООП.

Куда уж конкретнее.

Удобно это или нет это уже вкусовщина.

А кругозор стоит расширять. А не утверждать "А у меня в Бейсике Питоне сделано так, потому это аксиома"

Так вы еще и читать не умеете? Я вас разве спрашивал о списке языков в которых есть "расширенная диспетчеризация вызовов без классического ООП"? Нет. Я утверждаю что композиция это НЕ альтернатива наследованию если язык поддерживает наследование. Использование композиции в качестве альтернативы наследованию это костыли которые приводят к меннее наглядному, менее качественному и создают сложности с поддержкой кода. Если вы не согласны с моим утверждением, то жду ваших АРГУМЕНТОВ, а не списка языков и учебников.

Не согласен.

Как, очевидно, и разработчики вышеупонянутых языков.

Аргумент - они рекомендуют использовать композицию или же вообще не стали реализовывать наследование.

Я не так велик, чтобы выдавать свое мнение как единственно правильное =)

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

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


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

Почему бы ни создать кастомный класс SortedList c методами Get(i int) T, Append(v T) и тд и использовать его?
А в случае (практически невозможном в реальности), когда вам вдруг понадобится в одном проекте в одном наборе функций использовать и SortedList и обычный List - можно использовать те же интерфейсы.

И при каждом изменении интерфейса вы должны менять все где он упоминается и реализован.

Так это всегда нужно делать, если вы меняете интерфейс, то все функции, что используют этот интерфейс должны быть изменены. Но с наследованием же такая же история, если вы в List переименуете метод Get на GetValue, то вам везде придется поменять названия

Прелесть композиции и запрета наследования в Go в том, что Go гарантирует, что если у вашей функции есть 3 аргумента - структура, int и массив интов, то туда можно передать только структуру, int и массив интов, никаких дочерей, пасынков, родственников и друзей, только эти типы данных

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

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

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

можно использовать те же интерфейсы.

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

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

Да неужели? Если я добавляю 25 новых функций в базовый класс то я могу вообще не менять ни одного наследника и все будет рабоать. А в случае с интерфейсом я ОБЯЗАН менять ВСЕХ наследников причем в большинстве случаев с композицией это будет copy-paste.

Прелесть композиции и запрета наследования в Go в том, что Go гарантирует, что если у вашей функции есть 3 аргумента - структура, int и массив интов, то туда можно передать только структуру, int и массив интов, никаких дочерей, пасынков, родственников и друзей, только эти типы данных

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

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

Под классами я имел ввиду в Го, конечно, структуры, их тут часто называют классами, потому что и так понятно о чем речь (и методы, кстати, у структур есть)

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

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

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

Не надо вытаскивать слова из контекста, я писал " есть структуры в которых нет методов и для структур композиция это и есть частный случай наследования". Если не верите, то возмите на с++ и напишите наследование и композицию для структур без методов и вы увидите что результат будет одинаковый. Я это лично проверял как разные компиляторы c++ располагают классы, структуры и таблицы виртуальных методов в памяти еще 30 лет назад.

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

Выгоднее чего? Copy-paste реализации через интерфейсы. Так это аксиома что Copy-paste это плохо, а по другому там никак. Если вам нужны примеры, так откройте любую библиотеку на с++ где есть наследование и попробуйте подумать как это реализовать без наследования, а только композицией..да это будет возможно написать, но код будет намного сложнее чем через наследование.

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

В каком смысле "не нужно разбираться какая функция в реальности вызывается у класса"??? Если функция в Go принимает ссылку на интерфейс то как раз нужно разбиратся что за немойми что это за экземпляр и кто реализует этот интерфейс и какая в реальности функция вызывается. и вполне возможно что что кто-то в дочернем классе реализовал вместо интерфейся бог знает что

Да, в случае интерфейсов такое будет

А в случае НЕ интерфейсов в Го гарантировано передастсо именно то, что принимается, а не дочка

Ну а у в случае если вообще ничего не писать, так и вообще поддерживать легко...нет кода - нет проблем. Но мы же все таки пишем код и используем интерфейсы. Хотите реальный пример - есть у меня небольшой проект на Go - стриминг сервер, который раздает MPEGTS/HLS/SRT и т.п. Если бы я его писал на C++ то я бы сделал базовый класс входного потока и потом бы уже наследовался от него и реализовывал на его основе отдельно MPEGTS/HLS/SRT и т.п. Но в Go нет наследования и поэтому чтобы не плодить copy-paste реализуя все через интерфейс я переворачиваю эту модель задом наперед и делаю базовый класс входного потока финальным, а реализации MPEGTS/HLS/SRT через композицию в этот класс указателя на интерфейс имплементации конкретных протоколов,с неизбежными кросслинками между имплементацией и базовым классом. Вы думаете это упростило мне жизнь? да нихрена. Да все работает, нет copy-paste, но реализация в прямом смысле через зад - задом на перед. Это необходимые костыли, которые усложнили код и его поддержку.

А наследование подразумевает что родительский класс имеет полный доступ к наследнику

Вот тут что-то странное написано... Родительский класс, вроде, никакого доступа к наследнику не имеет. Он буквально не знает о его существовании же?

Ну и даже если исправить - наследник, например, к приватным полям родителя доступа не имеет.

Не все так однозначно, в общем. Да sealed/final не на ровном месте придумали - видимо, какую-то проблему решали? Может, есть она, все же?

А в каком месте вы привели пример Композиции на Go?

Если вы про этот код:

type UserService struct{	
  cache Cache
}

type Cache interface{	
  Get(k string) (string, error) 	
  Set(k, v string) error
}

type MapCache struct {/* ... */}
type MapCacheThreadSafe struct {/* ... */}
type RedisCache struct {/* ... */}

Так тут, пардон, — её нет, это Агрегация.

Но главный вопрос к "тоталитарности" интерфейсов в Go:

В чем преимущества "гадания" в Go — подходит ли структура под интерфейс, который нигде не задается явно при реализации, — перед явным объявлением в Python?

class Cache(abc.ABC):
	@abc.abstractmethod
	def get(self, k: str) -> str: ... 	    
	
	@abc.abstractmethod 	  
	def set(self, k: str, v: str) -> None: ...   
	
class MapCache(Cache):	  
	def get(self, k: str) -> str: ... 	  
	def set(self, k: str, v: str) -> None: ...   
	
class RedisCache(Cache): 	  
	def get(self, k: str) -> str: ... 	  
	def set(self, k: str, v: str) -> None: ...

В Python я вижу, что класс наследуется от Cache и согласно LSP обязан следовать контракту родителя.

В Go же, чтобы понять, что, например, структура MapCache вообще имеет отношение к интерфейсу Cache, мне нужно откуда-то догадаться, что где-то объявлен такой интерфейс.

Где здесь та самая "тоталитарность", которая якобы упрощает жизнь и снимает вопросы? И как насчет последовательности дизайна языка? Куда вдруг пропала явная типизация - и всплыла "уточка типизации"? : )

  1. Встраивание Кеша в UserService как раз пример композиции

  2. Тоталитарность Го тут проявляется в том, что в питоне есть 5 способов, как можно сделать одну задачу. В данном примере можно протоколы использовать, а можно асбтрактные классы. Один разраб одно будет использовать, другой другое. А в Го - только интерфейсы, даже если интерфейсы тут хуже, чем абстрактные классы

Не горячитесь так. Я сам был ярым адептом ООП много лет, начиная с Delphi3, потом плюсы, шарпы, джава. Потом как-то нелёгкая занесла в веб (PHP, JS, TS), потом и Go опробовал. И внезапно безальтернативный для меня ООП с наследованием, инкапсуляцией и полиморфизмом оказался всего лишь одним из способов решения одних и тех же технических и бизнесовых задач.

Оказалось, можно написать элегантный и весьма эффективный код и вообще без ООП, и даже получить от этого удовольствие.

А можно и с ООП красиво всё сделать.

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

Порой, это вообще вопрос культуры конкретного проекта, стека, команды.

Яко тебя помотало по миру языков программирования. Истинно, что для всего нужны свои тулы, и ООП не панацея от всего, но некоторые задачи, например, GUI хорошо ложатся на эту парадигму.

Вот например, есть у Гугла классный язык Go, и пишутся на нём хорошо бэки, но хочется и GUI, и мобилки, всё это, и изобретает Гугл ещё один язык - Dart. Казалось бы, пиши Flutter на таком классном языке Go, но нет, нужон ООП в полный рост!

ООП не панацея от всего, но некоторые задачи, например, GUI хорошо ложатся на эту парадигму.

Тоже так думал, прогая на Delphi/VCL. Но веб как-то размазал это всё. Там конечно тоже есть ООП, но и функциональщиной присыпано щедро. И декларативки много.

И как ни крути, но веб UI опережает все остальные варианты нынче, даже в десктопе

Flutter пока не трогал, не могу ничего сказать.

Не особо опережает. В Delphi сейчас, в новом, относительно, фреймворке, контролы ещё и собственный стиль (не просто картинку, не скин) могут иметь. Механика схожа с использованием CSS в себе, только этот стиль создаётся тоже визуально. Т.е. в обычном дизайнере строишь интерфейс, а в другом дизайнере можешь создавать стили для разных контролов или заменять базовые стили. Таким способом, я имитировал пиксель в пиксель интерфейс ChatGPT сервиса, в десктопном и мобильном клиенте, когда клиентов ещё не было.

Да, очень похожие механизмы я использовал в C++/Qt лет 15+ назад. Для стилизации под брендбуки - хорошая штука

Но все же под веб UI в мире ИТ на пару порядков больше инфраструктуры, знаний, разнообразных инструментов, на любой вкус, как ни крути.

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

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

До сих пор держит планку. Под Линукс, андроид и мак тоже быстро компилит, хоть и чуть медленнее, из-за llvm

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

В Го ничего менять не будут.

Хотите изменений при гошном синтаксисе - см vlang

Так и речь не о том, чтобы делать из Go язык на все случаи жизни, а о том, что автор сравнивает и "удивляется": и зачем в других языках все эти сложности, которых нет в Go? : ) Так это не потому, что Go нашёл программистский универсальный Грааль, а потому что это хороший инструмент для конкретной сферы. У других языков — другие сферы, отсюда и другой инструментарий.

Так я, автор, и не говорю, что Go это Грааль или идеал, а просто хороший инструмент под конкретную задачу

(надо было секцию "Минусы" побольше описать...)

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

Вы берете, условно говоря, трековую машину - с максимально обтекаемым силуэтом и прижатую к земле, у которой из салона выкинуто всё "лишнее" а вместо этого "лишнего" - установлен каркас безопасности... и сравниваете её с седаном, на котором ездят на работу каждый день, или с пикапом, в котором грузы возят - и говорите: во, зачем им этот салон, где усилители, почему такие высокие - их же раскачивать будет в поворотах!... : )))

И опять - речь не про то, что Go, условно - плох на "бездорожье" : ) ему туда по-сути и не надо, а о том, что если уж и имело смысл сравнивать - так это "машины" одного назначения.

А если делать "многоцелевую" машину - то вот тут и полезут компромиссы : )

Так Rust, Python и Java используют часто в тех же задачах, что и Go

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

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

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

Rust - и вовсе выбор для системного программирования. Да, и на нём можно писать всё остальное - но это, утрируя, как забивать гвозди микроскопом. На каком-то уровне - сойдет; если нужна максимальная эффективность - будешь страдать.

Или вы будете утверждать, что таки Go - это и великолепный швец, и жнец, и на дуде игрец, ибо реализовал "тоталитарность"? : )

Ява прямой конкурент Го.

Разве что создавалась в эпоху мейнфреймов в серьёзном бизнесе, и несёт многое оттуда.

А Питон это вообще не отсюда. Но его суют везде, с известным результатом.

Java моложе Python : )

И про мейнфреймы — это, мягко говоря, натяжка. Java создавалась как "write once, run anywhere" для разнородных систем, а не для мейнфреймов. Но это к делу не относится.

Суть в другом: и Java, и Python — языки широкого профиля, каждый со своей историей и компромиссами. Go — язык узкой специализации. И это не делает его "хуже" или "лучше". Просто разное назначение. А когда начинают сравнивать специализированный инструмент с универсальным и удивляться, почему у второго якобы "лишние" детали — это странно : )

Разве что создавалась в эпоху мейнфреймов

Ну это вы лихо хватили. Мейнфреймы это 70-80гг, программировали их на Фортране, Алголе и ещё кучке давно мертвых языков. Из тех времён уцелел пожалуй только С да ассемблер.

С чего бы?

IBM до сих пор живёт и здравствует.

А в 90х кроме мейнфреймов нормальных машин и не было вообще.

Я скорее к тому, что Go де-факто конкурирует с Python, Java и JS в сфере бекенд разработки при этом в одинаковых задачах.

Вот, например, нужно вам написать бекенд для нового интернет магазина, к вам придет CEO и будет докапывать, а что лучше использовать?
И вроде Java и Go по перформансу будут похожи, но в джаве магии больше, из-за чего что-то сложное сделать просто, но зато и что-то простое сделать сложно тоже легко. А в Go наоборот, и сложное и просто делается одинаково просто, но долго. (Грубо говоря)
А еще в дискуссию можно запихнуть Python и JS с TS и тоже сидеть гадать что лучше использовать

Посоветуйте статью от такого же увлечённого товарища о Rust

Время вкатывания в Go ~ 2 недели, в Rust ~ от 2х месяцев и больше. В мысле, если вы на чём то ещё раньше программировали, а не просто на пианино играли.

В этой статье рассматриваются плюсы и минусы языка, поэтому про Rust такую статью писать нельзя.

Про Rust можно писать только хорошее. Писать плохое – предательство. Дискредитация системы владения и заимствования – предательство. Сомнения в безопасности – предательство. Паника – это нормально, это для вашей же безопасности.

Зато как весело и спорно получится, столько срачей в комментах будет :)

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

Ну с РФ по тоталитарности не сравнится.

Sign up to leave a comment.

Articles