Pull to refresh

Comments 180

К сожалению, за ним не стоят большие корпорации, поэтому он не модный (
Он куда приятнее и проще чем Go и Rust.

Это надо читать как "приятнее чем Go и проще чем Rust"? Потому что иначе получается не особо похоже на правду, хотя и без этого спорно.

Там два утверждения. Вы про которое?

Да оба. Выбор основанный на вкусах и в принципе субъективный, собственно как и большая часть статьи.

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


Живой пример:


Go:


package main

import "fmt"

func int64Sum(list []int64) (uint64) {
    var result int64 = 0
    for x := 0; x < len(list); x++ {
        result += list[x]
    }
    return uint64(result)
}

func int32Sum(list []int32) (uint64) {
    var result int32 = 0
    for x := 0; x < len(list); x++ {
        result += list[x]
    }
    return uint64(result)
}

func int16Sum(list []int16) (uint64) {
    var result int16 = 0
    for x := 0; x < len(list); x++ {
        result += list[x]
    }
    return uint64(result)
}

func int8Sum(list []int8) (uint64) {
    var result int8 = 0
    for x := 0; x < len(list); x++ {
        result += list[x]
    }
    return uint64(result)
}

func main() {

    list8  := []int8 {1, 2, 3, 4, 5}
    list16 := []int16{1, 2, 3, 4, 5}
    list32 := []int32{1, 2, 3, 4, 5}
    list64 := []int64{1, 2, 3, 4, 5}

    fmt.Println(int8Sum(list8))
    fmt.Println(int16Sum(list16))
    fmt.Println(int32Sum(list32))
    fmt.Println(int64Sum(list64))
}

D:


import std.stdio;
import std.algorithm;

void main(string[] args)
{
    [1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln;
}

На удивление D в два раза короче, а код на Go чистой воды лапша.


Или вот парсинг аргументов командной строки:


Go:


package main

import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "os"
)

func main() {

    flag.Parse()
    flags := flag.Args()

    var text string
    var scanner *bufio.Scanner
    var err error

    if len(flags) > 0 {

        file, err := os.Open(flags[0])

        if err != nil {
            log.Fatal(err)
        }

        scanner = bufio.NewScanner(file)

    } else {
        scanner = bufio.NewScanner(os.Stdin)
    }

    for scanner.Scan() {
        text += scanner.Text()
    }

    err = scanner.Err()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(text)
}

D:


import std.stdio, std.array, std.conv;

void main(string[] args)
{
    try
    {
        auto source = args.length > 1 ? File(args[1], "r") : stdin;
        auto text   = source.byLine.join.to!(string);

        writeln(text);
    }
    catch (Exception ex)
    {
        writeln(ex.msg);
    }
}

По-моему Go улучшать бесполезно, его проще закопать.

надо добавить, на вскидку, перечисление возможных типов (как правильно называется?) при объявлении переменной, типа так
package main

import "fmt"

func intSum(list [](int8, int16, int32, int64)) (uint64) {
    var result int64 = 0
    foreach result += list[_]
    return uint64(result)
}

func main() {
    list := [](int8, int16, int32, int64) {1, 2, 3, 4, 5}
    fmt.Println(intSum(list))
}

и при компиляции генерировать несколько блоков, в данном случае intSum, и вызывать в зависимости от реально указанного типа, в данном примере их четыре, значит вызвать надо fmt.Println(intSum( четыре раза.
В итоге получится исходный вариант.
И с nullом также можно решить.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here

Это ж, как минимум, в арифметику надо уметь, а это не тренд. Скучней дженериков.

Про flag.Parse() пример искусственный. Короче написать так:
var test   string

func init() {
	flag.StringVar(&test, "test", "default", "help message")
        flag.Parse()
}

func main() {
        // Используем переменную
}


Это же из коробки дает и usage.
По-моему Go улучшать бесполезно, его проще закопать.

Расскажите это разработчикам Docker, Prometheus, Kubernetes, Juju, Grafana и прочим.

Ваши примеры очень хороши как раз тем, что показывают "лапшеобразность" Go в искусственно придуманных примерах. Такие примеры часто встречаются в академической среде, но не в практической разработке. Возьмём ваш первый пример, где вы реализовываете функцию суммирования для 4х различных типов — int8, int16, int32.и int64. На практике мы всегда работает с конкретными типами и код пишется, отталкиваясь от типа данных, а не наоборот. Если вам нужна абстрактная возможность писать дженерик код, а потом думать о данных, с которыми вы работаете, то это совершенно ошибочное представление о том, что такое программирование. И Go эксплуатирует эту особенность (работу с конкретными типами в большинстве случаев) для компромисса "простота/скорость <->. дженерик типы".


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

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

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


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

Такие примеры часто встречаются в академической среде, но не в практической разработке
Если писать только Hello World'ы то да, а если выполнять минимальную работу с данными, то возможность делать обобщения это это не просто здорово, а неимоверно круто. Код — не самоцель. Чем меньше кода, тем лучше. Куча народу пишет на Python потому что он позволяет коротко и компактно выражать мысли и вопрощать идеи, а не тратить время на борьбу с языком.

У Go нет ни единого реального преимущества перед D, кроме поддержки Google. Если вы планируете писать проекты в 100-150 строк кода, то возможно Go и будет хорошим вариантом, но вот когда проект начнет расти вы от простыней кода будете сходить с ума. Тем более синтаксис D куда привычнее для программистов на Си-подобных языках чем Go-шный.

У Go нет ни единого реального преимущества перед D, кроме поддержки Google.

Вы прямо по учебнику :)


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

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

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

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

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


Да и ближайшей замены go не видно в его нише простого языка для сетевых сервисов.
Как минимум D и Nim
Про Nim не слышал посмотрю спасибо, но D мне кажется все таки по духу ближе к c++, но это чисто по ощущениям. (а с++ точно не ассоциируется с простым языком)
D мне кажется все таки по духу ближе к c++
D проще C#. C# сложным языком не считается.
Почему-то авторы больших проектов на Go с вами не согласятся
Зато в коде на Go разберётся даже индус пятиклассник.
Go конечно суровый язык, но по моему вы слегка преувеличиваете.
Я немного поправил ваш вариант с суммированием, максимально не используя ни чего выходящего за за спеки языка:
package main

import "fmt"

func Sum(slice []uint64) (acc uint64) {
	for _, v := range slice {
		acc += v
	}
	return acc
}

func main() {

    list8  := []int8 {1, 2, 3, 4, 5}
    list16 := []int16{1, 2, 3, 4, 5}
    list32 := []int32{1, 2, 3, 4, 5}
    list64 := []int64{1, 2, 3, 4, 5}

	data := make([]uint64, len(list8))
	for k, v :=range list8 {
		data[k] = uint64(v)
	}
	fmt.Println(Sum(data))
	for k, v :=range list16 {
		data[k] = uint64(v)
	}
	fmt.Println(Sum(data))
	for k, v :=range list32 {
		data[k] = uint64(v)
	}
	fmt.Println(Sum(data))
	fmt.Println(Sum(list64))
}

Что касается аргументов командной строки, как я понял, вы просто передаете имя файла и с ним работаете, тогда имя файла можно получить проще: я вот об этом — https://gobyexample.com/command-line-arguments

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

Go следит за типами, поэтому и приходится явно приводить тип (Да, да, да неявного приведения типов нет), и поэтому приходится выделять память.

Да только и вы лукавите: [1, 2, 3, 4, 5] — это массив целых какой размерности, int8, int16, int32, или 64 бита? Ваш вариант должен быть чуть побольше, хотя да, он будет все еще лаконичнее Go.

Для суммирования, кстати, есть стандартный алгоритм, так что нет надобности велосипедить:


import std.stdio;
import std.algorithm;

void main()
{
    byte[] list8 = [1, 2, 3, 4, 5];
    writeln( list8.sum );

    short[] list16 = [1, 2, 3, 4, 5];
    writeln( list16.sum );

    int[] list32 = [1, 2, 3, 4, 5];
    writeln( list32.sum );

    long[] list64 = [1, 2, 3, 4, 5];
    writeln( list64.sum );
}
// ...
data := make([]uint64, len(list8))
// ...

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

Просто демонстрировать такой плохой код как решение задачи без соответствующих ремарок (а ведь новички могут его найти и использовать не задумываясь) — стыдно.
Есть конкретная задача, посчитать сумму элементов КОНКРЕТНЫХ массивов, и сделать это максимально лаконично, просто и понятно. Да, по хорошему следовало бы каждый раз заново выделять память под слайс. По поводу принципиальной необходимости это делать: если вы знаете способ написать сумматор для массива произвольных целых, не прибегая к рефлексии или другим нетривиальным приемам, я с удовольствием возьму ваш метод на вооружение. Я ни когда не утверждал, что я гуру разработки на Go. Я тоже учусь, и делаю это при любой возможности.
В вашей интерпретации правильный ответ был бы просто «fmt.Println(15)».
Это не «конкретная» задача, а вполне себе «абстрактная», которая демонстрирует слабость синтаксиса.
UFO just landed and posted this here
Windows тоже использует большинство, и это лучше его никак не делает

Самое главное почему используют go — это горутины и идущий с ними в комплекте планировщик. Этот планировщик позволяет запустить N горутин на M потоков и скорость полученного решения будет очень высокой, а сложность разработки низкой. Я так понимаю, что даже если в 20 стандарте С++ примут корутины планировщик все равно надо будет дополнительно разрабатывать. Поэтому в идеале было бы здорово иметь go в качестве библиотеки для С++ приносящей скорость и легковесность горутин в C++

планировщик все равно надо будет дополнительно разрабатывать

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

Не стоит отрицать и того, что Go взлетел потому что низкий порог вхождения (ну по сравнению с тем же rust) и потому что google.
UFO just landed and posted this here

Отсюда кстати проистекает еще один минус языка. Вся документация, все примеры негласно сводятся к одной папке содержащей main.go и main_test.go. Если начать разбираться то вылезают следующие ограничения:
1) относительные пути импорта не рекомендуются и могут быть выпилены в будущем
2) импортировать надо относительно GOPATH и названия package либо просто от названия package
3) В одной папке могут быть файлы только одного package

3) В одной папке могут быть файлы только одного package

В Java тоже. Кому это мешает?
UFO just landed and posted this here
Относительные пути выпиливаются в пользу вендор. Нужно лишь добавить папку vendor внутри проекта, создать в нем нужные пакеты и можно обращаться к ним без указания каких либо путей. Так-же возможно склонировать пакет, например, с github и заморозить его на нужном коммите в vendor.

На самом деле я так и не нашел совсем правильного варианта как это должно выглядеть. Вот предположим у меня есть 3 сервиса, 10 моделей, 10 контроллеров. Получается что под каждую сущность мне нужно создавать отдельный пакет и их всех положить в соответствующую папку внутри vendor? Более того чтобы проект оставался go get'able под каждую сущность нужен свой репозиторий?

Я в таком случае создаю пакет controllers, где каждый элемент описывается в отдельном одноименном файле и представляет собой объект с необходимыми методами и свойствами.
Либо же вообще все ложу в корень проекта, применяя различные способы написания для файлов. Например users.go, Users.go. Это вполне нормальная практика для go, но по началу коробит многих, кто пришел с других языков, например с той же ноды.
В общем единого верного решения наверное не существует.
Либо же вообще все ложу в корень проекта, применяя различные способы написания для файлов. Например users.go, Users.go. Это вполне нормальная практика для go, но по началу коробит многих, кто пришел с других языков, например с той же ноды.


Я попадаю под «пришедших с других языков», но это действительно потом нормально поддерживать?

Расскажите, весело ли эти "users.go, Users.go" собирать на MAC OS X (на системном разделе) и Windows? Если бы такой "нормальной" практике следовали многие, то go был бы существенно менее распространён.

Я имел в виду, что нормальной практикой является держать .go файлы в одном каталоге, особенно в небольших проектах. В моем случае это сервисы под linux-сервера и именование user.go и User.go вполне устраивает. Но, конечно же, это могут быть user.go и user-model.go и т.д. Ничего не имею против разделения по каталогам, все зависит от проекта и поставленных задач.
предназначенном (по словам Роба Пайка) для написания сервер-сайд приложений на 100-150 строк кода?

Если бы на нём только такие и писали, то и срачей не было бы. (:

предназначенном (по словам Роба Пайка) для написания сервер-сайд приложений на 100-150 строк кода?

А можно, пожалуйста, ссылку?
UFO just landed and posted this here
Автору статьи и прочим хипстерам...

Сразу с козырей решил зайти?


P.S. Пишу на расте на работе за деньги. Хотя "взлетевшим" язык тоже не считаю, но надеюсь, что это вопрос времени.

А уж как в свое время взлетел PHP…

Т.е. я не хочу ничего плохого говорить про PHP или иной язык, но оценивать качество языка по его распространению… ну не знаю.

Быстрый взлет говорит не о качестве, а о простоте вхождения.

Еще очень сильно убивает отсутствие модификатора const для переменных

«const» и «переменная».

Ну вот никак не вяжутся эти два слова. Я бы слово «переменная» заменил на «связывание» (в данном контексте). Т.е. изменяемое связывание(mutable) и неизменяемое(immutable).

Да, спасибо за поправку, имелись ввиду неизменяемые ссылки на переменные

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

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

А можно какой-нибудь пруф данного утверждения?

https://github.com/golang/go/wiki/CodeReviewComments#pass-values
http://goinbigdata.com/golang-pass-by-pointer-vs-pass-by-value
Так же был доклад, уже не вспомню чей, где в результате профилирования было показано, что передача по значению быстрее (а по ссылке, естественно, позволяет несколько экономить память)

Ок. Давайте посмотрим цитаты из Ваших ссылок
1) This advice does not apply to large structs, or even small structs that might grow.
2) If variable is a large struct and performance is an issue, it's preferable to pass variable by pointer. So that to avoid expensive copying of the whole struct in memory.
Естественно, передавать int по ссылке бессмысленно. Но, если реализовывать нормальный dependency injection, то передача по ссылке критична. Кроме того, у меня сложилось мнение что в golang при возврате значения нет move семантики и поэтому возвращать из функции, аналога конструктора, лучше тоже ссылку

Если нужна неизменяемая структура, то можно можно использовать неэкспортируемые поля.
Вот, вспомнил ещё:
5. Objects that do not contain any pointers (note that strings, slices, maps and chans contain implicit pointers), are not scanned by garbage collector. For example, a 1GB byte slice virtually does not affect garbage collection time. So if you remove pointers from actively used objects, it can positively impact garbage collection time. Some possibilities are: replace pointers with indices, split object into two parts one of which does not contain pointers.

https://software.intel.com/en-us/blogs/2014/05/10/debugging-performance-issues-in-go-programs
Единственное — GC заметно улучшился с тех пор.
Если для задач автора Rust подходит больше и он достаточно в нем разбирается, чтобы написать статью — почему бы просто не пользоваться им и не забыть про Go?

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

Так статья называется «Что бы я изменил в Go». Получается, что автор предлагает из Go сделать копию уже существующего языка.
Go, как и большинство языков, так или иначе позаимствовал ряд концепций из других языков, существовавших до него. Концепции, реализованные в Rust — тоже не с нуля придумали. Внедрить пару удачных концепций, показавших свою высокую полезность на практике, и избавиться от неудачных, продемонстрировавших свои недостатки — это не значит сделать копию другого языка. В конечном итоге у Go есть свои принципы и цели, отличные от других языков, и предложенные концепции могут быть внедрены достаточно оригинально (и как минимум потребуют этой оригинальности и вдумчивой адаптации под уже существующие особенности языка).
Что только люди не изобретают, лишь бы не изучать С++.

В C++ на мой взгляд на текущий момент самая большая проблема это система сборки и отсутствие единого пакетного менеджера. В результате каждая сборка крупного проекта на C++ приносит новую порцию "радости". Особенно чувствуется когда надо собрать проект рассчитанный на, скажем, Ubuntu под Windows. Начинаются всякие пляски с cygwin, mingw итд при том что в целом по синтаксису gcc не сильно отличается от msvc.

Это проблема, но она частично решается административными методами, когда на старте проекта приходит техлид с N годами опыта и говорит «так, мы используем вот этот компилятор, эту IDE, эту систему сборки, собираемся под такие и такие платформы, все несогласные — в сад!». Потому что спорить можно до бесконечности, а работу нужно делать уже вот сейчас.

Давайте рассмотрим пример. Вот есть С++ проект https://github.com/BVLC/caffe (нейросети). Под linux/osx master ветка. Под windows создана отдельная ветка поддерживаемая microsoft причем вангую что ветка под windows никогда не попадет в master. В master ветке сборка идет через make или cmake. В windows ветке сборка идет через cmake/ninja. Управление зависимостями тоже не универсально. Под linux apt-get с перечислением зависимостей вручную. Под osx mac ports с перечислением зависимостей вручную. Под винду вообще python скрипт скачивающий prebuilt зависимости (https://github.com/BVLC/caffe/blob/windows/scripts/download_prebuilt_dependencies.py). Более того, если посмотреть список полученных зависимостей, то во-первых часть из них .lib, а часть с расширением .a (что говорит о сборке через cygwin), а во вторых часть статических библиотек идет с префиксом lib часть без. По мне так это называется бардак, который сказывается на том, что большую часть времени приходится заниматься сборкой, а не собственно программированием. При этом во многих языках эти проблемы решили. Взять например cargo, или на худой конец npm. Тот же Go при всех его недостатках если не использовать cgo гарантированно соберется на osx/linux/win без дополнительных танцев с бубном

Всё верно, и виной тому — отсутствие стандартов.


  1. Стандартная библиотека очень мала, поэтому приходится либо использовать непереносимый системо-зависимый API, либо использовать зоопарк сторонних бибилиотек. Но это сделано сознательно, потому что C/C++ изначательно позиционируются как расширяемые языки, и программист волен сам выбирать библиотеки под свои нужды.


  2. Нет единого стандарта описания проектов, каждая IDE использует свой собственный формат, масла в огонь добавляют всякие надстройки типа cmake, которые не являются полноценными системами для описания проектов. Вообще, я считаю, что make нужно использовать только для сборки продукта из готовых составных частей, но никак не для компиляции.


  3. Нет единого стандарта хранения и подключения статических библиотек по типу jar-файлов или сборок .NET. Теоретически, платформо-независимые модули С++ должны решить эту проблему, но их пока ещё нет.


  4. Ну и закончить можно тем, что в С++ даже не зафиксированы размеры встроенных типов данных. Приходится использовать костыль в виде stdint.
  1. Это совсем не обязательно проблема. Стандартная библиотека раста, пожалуй, ещё минималистичнее, но вопрос решается как раз через лёгкость подключения библиотек.


  2. Cmake, как по мне, проблем не добавляет, скорее наоборот. Да, он совсем не идеален, но получил хорошее распространение и может стать стандартом де-факто. И лучше иметь такое решение, чем никакого.


  3. Не уверен, что "стандарт хранения" так уж нужен для нативных библиотек. Да, есть нюансы, но даже вариант тупо собирать из исходников здорово упросил бы жизнь во многих случаях…
    А модули — это ведь тоже про исходники или вместе с ними фиксируется и какое-то представление собранной либы?


  4. Не сказал бы, что это проблема. Вернее, да, неаккуратно использование таких типов может усложнить жизнь, но мне кажется, что опытные программисты на С++ привыкли думать об этом моменте. А у неопытных будет немало других проблем. (:
    Ну и почему stdint костыль?
  1. Так я и не считаю это проблемой. Наоборот, удобно, что на C++ можно писать хоть под контроллеры, где просто из-за недостатка памяти нет возможности использования большой стандартной библиотеки.


  2. Вот в итоге должен остаться кто-то один.


  3. Я бы хотел видеть промежуточный вариант между исходниками и скомпилированной нативной либой. Но пока нет стандарта того, что есть модули, сложно ответить на данный вопрос. Хотелось бы видеть упаковку файлов либы в некоторое платформо-независимое представление.


  4. Ну int32_t по сравнению с int выглядит как-то нелаконично. Ну и мелочь, а неприятно: использование беззнаковых типов для хранения размеров в стандартной библиотеке. Смешение знакового и беззнакового кода небезопасно. Java же как-то живёт без unsigned long long.
Ну int32_t по сравнению с int выглядит как-то нелаконично.

Ну пару лишних символов не проблема, как по мне. Но растовые i8, u16, i32 и т.д. больше по вкусу. Плюс платформозависимые isize/usizeдаже писать дольше, что тоже неплохо.

UFO just landed and posted this here

Мне ещё синтаксис С++ неприятен — из-за того, что язык развивается путём нагромождением функционала поверх старых версий, он получается слишком многословен.

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


fn closed<F: Fn(&Rc<RefCell<S2>>) + 'static >(f: &F)

Особенно тяжело было смириться с плюсом в типе переменной))) Но со временем привыкаешь и становится даже удобно

У раста ещё есть хороший подход убирать конкретизацию типа под выражение where:


fn closed<F>(f: &F) where F: Fn(&Rc<RefCell<S2>>) + 'static 

Тогда само определение функции становится более лаконичным.

Можно, но куда приятнее писать:


(x, y) => x + y

чем


[](auto x, auto y) { return x + y; }

И это ещё стоит сказать спасибо новому стандарту, поддерживающему generic lambda, в C++11 вместо auto пришлось бы указывать тип явно (пример), либо объявлять шаблонную функцию:


template <typename S, typename T>
auto add(S a, T b) -> decltype(a + b) { return a + b; }
А есть конкретные предложения, как бы можно было развивать язык и избежать многословности?

Мой вариант предложения, заменить int, long, float и тд на типы раста с учётом популярных платформ. то есть float == f32; unsigned char==u8 и тд.
Возможно при этом надо добавлять в начало файла что-то вроде #pragma version 2020

Ваше предложение решается простым инклюдом.


А я имею в виду такие мелочи, как:


1.Ограничение действия using namespace текущим файлом, а не единицей трансляции. В текущем варианте эта директива неюзабельна, потому что распространяется и дальше и может привести к конфликту имён. Тогда можно будет смело лепить using namespace std; в каждом файле и не замусоровать код std::, std::chrono:: и т.д.


2.Ещё более сокращённый синтаксис для лямбд: возможность опускать слово auto, возможность опускать фигурные скобки и слово return, если лямбда состоит из одного предложения, т.е. вместо


[](const auto &x, const auto &y) { return x + y; }

иcпользовать вариант


[](const &x, const &y) => x + y;

Здесь стрелка => просто заменяет { return ... }.
Квадратные скобки, к сожалению, никуда не деть — будет конфликт с существующим синтаксисом.


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


3.Замена static_cast<...>, reinterpret_cast<...>, dynamic_cast<...> на что-нибудь более удобоваримое, например, C-style cast — только static_cast, т.е. static_cast<T>(x) меняется на (T)x, dynamic_cast<T>(x) меняется на x as T, а reinterpret_cast<T> можно оставить как есть.


4.Добавление расширений (extensions) в стиле C#. Операторы можно перегружать отдельно вне класса, шаблоны специализировать отдельно, а добавлять методы — почему-то нет. Непорядок. Ведь написать a.sort(); сильно короче и читаемее, чем std::sort(a.begin(), a.end());.


Может, и ещё что вспомню в процессе написания кода. А если рассматривать язык в целом, то сильно не хватает функциональщины, корутин, концептов. Так, код, написанный на C# в 3 строчки, на C++ вполне может представлять собой императивщину на целый экран.

UFO just landed and posted this here
Лично мне больше всего не хватает возможности задавать значения по умолчанию для аргументов функций и полей структур. Самое забавное, что внутри компилятора аргументы функций как раз хранятся так же, как и структура :)
Эмоциональная статья ни о чем.

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

Источники приведенные в начале статьи приводят подобные авторским рассуждения, на тему «почему Go это не C/C++», почему «Go это не Haskell», на мой взгляд — абсолютно субъективные.
В общем, бросил читать после 4 или 5 абзацев.

Да, Go имел и имеет свои болячки, от некоторых он избавился, другие ждут своего часа, третьи не будут исправлены никогда. Ну не понравился вам Go, не используйте. Участвуйте в Rust комьюнити, выдвигайте свои предложения, а лучше делайте свои комиты в репо проекта и будет всем счастье.
> статья ни о чём

> бросил читать после 4 или 5 абзацев

Вы точно уверены, что «пустой файл» и «ошибка чтения» — это одно и то же? :-D
Ок перечитал статью от начала и до конца. Стало только хуже.

Отказ от перехвата исключений в пользу явной передачи ошибочных результатов.

panic/recover?
Упор на многопоточность.
Очень категоричное утверждение. Поддержка многопоточности не означает, что вы должны ее использовать. Как по мне, так Go — это Си без плюсов, но с немного странным синтаксисом и да, поддержкой многопоточности.
В Rust есть поддержка операций реального времени, при необходимости он способен оперировать только стековой памятью. В Rust сложная система типов, которая может, к примеру, выявлять проблемы посредством многопоточного (concurrent) обращения к общим данным в ходе компилирования.
Эти предложения только мне кажутся странными?
Объектно ориентированные идеи в нём видоизменены таким образом, что они стали более доступны для программистов, знакомых с другими объектно ориентированными языками.
Сами авторы языка говорят об отсутствии ООП в Go и только об эмуляции некоторых аспектов ООП дизайна.
Про легковесные акторы коллеги уже обращали внимание :)
На мой взгляд, можно внести в язык изменения, облегчающие проверку типов, чтобы вылавливать проблемы на стадии компилирования.
Вот интересно, автор слышал про go vet…? (Если нет, то посмотреть здесь: https://golang.org/cmd/vet/)
Хотя наверное в чем-то упрек можно признать справедливым. Я не гуру Go, я только учусь.
А что насчёт начального значения (zero value)? Каким оно будет для функционального типа, интерфейсного типа без nil? Думаю, что начальные значения — тоже плохая идея.
Автор подмечает косяк в дизайне языка, и тут же все портит излишне категоричным утверждением.
Одно из архитектурных решений в Go — требование чётко прописывать каждому типу значение по умолчанию, так называемое нулевое значение.
Это вообще не понял о чем. Я легко могу объявить структуру указав типы для ее полей, или речь о чем — то еще?
Но есть и другое решение, лучше обеспечивающее безопасность кода: в Rust, Flow и других языках для выявления использования неинициализированных переменных применяется анализ потока данных. И если таковые факты обнаруживаются, то возникает сбой проверки типа.
Опять странные утверждения. Автор наверное имел ввиду автоматический вывод типа переменной исходя из ее начального значения, и даже если так, как и всякий автоматический алгоритм этот тоже может ошибаться.
Манипулирование списком непрактично
Правилно сказать, что в Go отсутствуют шаблоны, в том виде, как это понимается в том — же C++. Более специализированные функции вполне реализуемы.
Ещё одна пощёчина Go от функционального программирования
Повторюсь: кто вам сказал что Go является языком ФП?
Возможно, неплохо бы использовать мьютекс для обновлений docs или для отправки результатов из горутин через канал обратно в основной поток выполнения.
Передача через канал атомарна, а вот изменение данных вне контекста из go-рутины без защиты мютексом — это да, ошибка. Даже базовые типы, вроде мапы или списка не являются потокобезпасными.
В Go есть «магическая» функция make. Похоже, она умеет делать с конкретными типами всё, что хотят авторы стандартной библиотеки.
А вот и не правда. В документации сказано: The make built-in function allocates and initializes an object of type slice, map, or chan (only), что в моем вольном переводе звучит как «функция make предназначена только для создания экземпляров slice, map или chan».
Что ещё важнее, range можно применять только к типам из стандартной библиотеки.
Подозреваю что нет ни какого секретного заговора, просто нет поддержки шаблонов а-ля C++, вот и все объяснение.
Ещё одна привилегия заключается в том, что только типы из стандартной библиотеки могут сравниваться с помощью ==, >, и т. д.
Рискну предположить, что операции сравнения реализованы только для стандарных типов, вот и все объяснение.
Возможно авторы отложили на потом решение вопроса: являются ли равными 2 инстанса структуры, если все значения их полей одинаковы. В скриптовых языках, вроде php или python, ответ на этот вопрос очевиден, с Go это не так.
Модуль проверки типов, способный находить ошибки при компилировании, выступает в роли дополнительного тестового набора, который всегда проверяет каждое сочетание условий.
Допускаю, что Rust выполняет более тщательную проверку, но говорить о полном отсутствии контроля со стороны Go нельзя.
Да, go требует отдельного телодвижения, в виде запуска go vet ..., и не очень понятно, зачем это было сделано.
Нехватка высокоуровневого параллелизма и средств многопоточного программирования
Согласет отчасти. Да, хорошо, когда есть качественный и оптимизированный код, которым можно восползоваться, но должен ли он быть частью языка?

PS Я прилично знаю Go, немного Rust. Честно говоря, за Rust я не взялся, только потому, что сами создатели еще недавно говорили, что Rust-у в продакшен пока рано.
Лично меня автор не убедил, в том, что я не прав. Да, оба языка «из коробки» имеют поддержку паралельного исполнения кода. Автор ни чего не сказал про конкурентность.
Rust в этом смысле выглядит перспективнее, но обоснованно говорить об этом, как мне кажется, пока рано.

PPS Пока, мне реально не хватает возможности контролировать судьбу горутин.
Да, ты ее запустил, и все. Работает она, упала с ошибкой, заблокировалась, ты узнать не можешь, если сам не закодировал. Ты не можешь ее даже завершить, если об этом заранее не позаботился.
Опять же, если говорить о многопоточности, немного напрягает двойственность каналы\и\или\мютексы. А если использовать и то, и другое, как это отражается на планировщике?
panic/recover?

Ну да, есть такое, но вроде как, не поощряется на этом логику строить? Честно говоря, далёк от Go, но могу высказаться про Rust, раз уж автор его сам часто в пример приводит. Там тоже постулировался "отказ от исключений" и поначалу было немало ограничений: перехват/обработка только на границе потоков, "нестабильность" (наличие только в найтли билдах) данной фичи и т.д. Затем видимо пришло понимание, что в ряде случаев оно всё-таки нужно.


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


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

Я не спорю, но можно ссылку? А то на глаза попадалось многожество статей с весьма различными выводами. Бегло просмотрел официальный FAQ и ничего не нашёл.


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

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


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

Нет.


let a = if ... {
    10
} else {
    20
}

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


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

Или (дженерики) в C#, Rust и т.д. С++ тут (если не брать во внимание "некоторые нюансы") не уникален.


Повторюсь: кто вам сказал что Go является языком ФП?

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


А вот и не правда.

А где тут противоречие? В том, что поведение задокументировано? Претензия ведь в том, что самому аналогичную функцию написать нельзя.


В скриптовых языках, вроде php или python, ответ на этот вопрос очевиден, с Go это не так.

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

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

То что сами авторы говорят о языке: https://golang.org/doc/faq. Искать по строке «Is Go an object-oriented language?».

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

А где тут противоречие? В том, что поведение задокументировано? Претензия ведь в том, что самому аналогичную функцию написать нельзя.
не возьмусь высказывать предположения, почему постройка этих типов вынесена в отдельную функцию, но для всех остальных типов есть new. Не нужно в особенностях дизайна искать более глубокий смысл, чем есть на самом деле.
Можно объяснить чем так уникальные скриптовые языки и чем выделяется Go?
похоже брякнул не до конца подумав. Имел ввиду, что скриптовые языки для сложных типов данных используют copy-on-write, а Go сразу выделяет память, но это не аргумент.
Про panic/recver и в принципе эксепшены, они есть, ими можно пользоваться, призывов этого не делать я не встречал, да честно говоря и не искал.

Как минимум, заложена возможность в Cargo.toml прописать panic="abort" и отключить всю раскрутку. Уже из-за этого на нее наличие далеко не везде можно полагаться.

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

А как библиотека может давать такие гарантии? Ну и в C#, Java исключения в библиотеках очень распространены. Даже в C++ такое местами встречается.


Искать по строке «Is Go an object-oriented language?».

Как по мне, воспринимать это можно неоднозначно. В том смысле, что они так и говорят "и да и нет", а не "нет, Go не ООП язык и всё".


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

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


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

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

похоже брякнул не до конца подумав. Имел ввиду, что скриптовые языки для сложных типов данных используют copy-on-write, а Go сразу выделяет память, но это не аргумент.

А кто использует CoW для таких кейсов? Я больше встречаю просто передачу по ссылке (ruby, python, js). Если вы имели что-то другое ввиду — please, elaborate.

Не смотрел про 7, но 5 пых вроде как использует CoW, судя по существованию copy/deepcopy в питоне, та же фигня. Я уже признал, что неосторожно попутал ссылки на один инстанс и 2 разных инстанса с одинаковыми значениями свойств. Не вижу смысла продолжать.
использует CoW, судя по существованию copy/deepcopy в питоне, та же фигня

copy/deepcopy не говорит об использовании CoW ровным счётом ничего или слабо говорит в пользу его отсутствия. При наличии CoW a = {'x': 3}; b = a; a['x'] = 5; оставит в b {x: 3}, чего, естественно не происходит в случае python'а (вместо dict'а можно взять простой объект).

panic/recover?

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


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

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


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

Что странного в первом предложении?
Во втором — таки кривой перевод.


«В Rust сложная система типов, которая позволяет, например, выявлять проблемы в случае конкурентного обращения к общим данным ещё на этапе компиляции».


об эмуляции некоторых аспектов ООП

Ходит как утка, крякает как утка :-)
Нет смысла спорить о самой правильной терминологии, для пользователей это выглядит как ООП, поэтому это всё равно будут называть ООП.


и тут же все портит излишне категоричным утверждением

Но нас же интересует вопрос косяков в дизайне, а не эмоции автора?


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

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

По ходу речь не про типы, речь про значение по умолчанию для новосозданного типа. Эквивалентом в Rust была бы реализация типажа Default и конструирование нового типа через MyStruct::default(), однако Default реализуется далеко не для всех типов и его не всегда можно реализовать.


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

Нет, речь идёт про факт инициализации переменной, независимо от её типа.


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


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


Правилно сказать, что в Go отсутствуют шаблоны, в том виде, как это понимается в том — же C++. Более специализированные функции вполне реализуемы.

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


кто вам сказал что Go является языком ФП?

Претензия автора как раз в том, что Go далёк от ФП, когда этого ФП в Go так не хватает.


А вот и не правда. В документации сказано [...]

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


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


вот и все объяснение

Что и является сутью претензии, а не теорией заговора.


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

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


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

Допускаю, что Rust выполняет более тщательную проверку, но говорить о полном отсутствии контроля со стороны Go нельзя.

Тут опять же, речь немного о другом. В Rust тоже есть динамическая диспетчеризация, проверки в рантайме, и они несут с собой те же самые проблемы. Просто технически невозможно проконтролировать то, что заранее неизвестно (например, значение переменной из пользовательского ввода).
Но Rust предоставляет больше инструментов для статических проверок во время компиляции, применение которых он всячески поощряет.
Как недостаток автор приводит то, что в Go нельзя воспользоваться такой же стратегией исключительной проверки ограниченного множества вариантов и быть 100% уверенным, что в случае рефакторинга, когда добавляется дополнительный вариант в этом множестве, компилятор автоматически выявит все места, где проверка поломалась, поскольку перескок на дефолтную ветку — он технически корректен, и невозможно быть уверенным, что задумано было не так.
Претензия автора в том, что Go слишком уж полагается на проверки времени исполнения и это существенно сказывается на производительности и качестве кода, когда ошибки выпрыгивают во время работы лишь при определённых условиях, а не выявляются во время компиляции со 100% гарантией.


Да, go требует отдельного телодвижения, в виде запуска go vet ..., и не очень понятно, зачем это было сделано.

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




Ну и если подсуммировать, то один из выводов можно свести к следующему: если инструмент прост в изучении и имеет низкий порог вхождения (именно так позиционируют сейчас Go), это не значит, что он будет так же прост в сопровождении развивающихся продуктов с растущими требованиями и сложностью. Здесь уже в комментариях прозвучал аргумент, что «язык, предназначен (по словам Роба Пайка) для написания сервер-сайд приложений на 100-150 строк кода». Возможно, что в этих условиях он вполне себе хорош, поскольку всё, что находится в таком приложении вполне себе можно удержать в памяти. Но обычно такие условия подразумевают тонну уже готовых "батареек" на все случаи жизни и большой выбор библиотек. И вот уже изготовление этих библиотек вызывает сложности, многие библиотеки достаточно быстро перерастают далеко за тысячу строк и нуждаются в обобщении типовых ситуаций в коде (привет дженерикам). А авторы стандартной библиотеки элементарно не могут «объять необъятное». Да и серверные приложения сегодня совсем не то, что они были лет 10 назад, когда могли рождаться основные идеи языка. Основная мысль автора — Go очень не хватает элементарных инструментов для качественного сопровождения крупных проектов, которые, вроде, не так уж и сложно внедрить, просто посмотрите на другие языки (go vet, возможно, может решить часть описанных проблем, но очевидно, далеко не все и не так исчерпывающе).


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

Ок. Спор постепенно мигрирует в сторону холивара, и это пора прекращать.

Go вышел в прод раньше Rust, и возможно это не пошло ему на пользу.

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

Что же касается нововведений, о которых сейчас любят говорить, дженерики, функциональная парадигма, дальше добавить по вкусу, то я за разумный консерватизм. Rust заявляет о бесплатной поддержке абстракций, ура, я за, когда мне это потребуется, я вспомню о Rust. Для сайта я, почти всегда, возьму питон или пых. Если мне потребуется обработать большой файл с данными, то это однозначно будет perl. Go тоже имеет свою нишу, и каждый его пользователь, скорее всего, обозначит ее по своему, Для меня — это сервис, который можно быстро сделать и запустить, потребности в чем — то большем пока не возникало.
Erlang и Scala поддерживают упрощённую многопоточность, как и Go. При этом они прекрасно подходят для функционального программирования.
Это вот что имелось в виду?
UFO just landed and posted this here
Как ни статья, про Go, так какую-нибудь ерунду про Erlang обязательно напишут. В прошлый раз написали, что в Go ТОТ ЖЕ принцип многозадачности, что и в Erlang. Сегодня Erlang приравняли к Go. Что будет завтра? Наверняка и Scal'истам тоже есть что сказать.

Зайдем в Go дайджест. События, статьи, интересные проекты из мира Go [15 — 30 марта 2017]. Что мы там видим? Go, Haskell и котята — Episode 0135.

«Темы выпуска: Обсуждаем, как делать правильное приложение на Go; сравниваем AOT и JIT компиляцию на фоне релиза scala-native; смеемся над сложностями функционального программирования; придумываем, как применить язык с управлением эффектами; смотрим на чужой опыт работы на Go; слушаем котят; отвечаем на вопросы слушателей»

Ну то есть, для ушей Go'шника есть подкаст, в котором смеются над сложностями функционального программирования. В этой статье написано, что Erlang — это то же самое, только в нем еще ФП. Про которое ему объяснили, что оно не нужно. Декларативный взгляд на Erlang? Потом еще тесты hello world приложить, где Go оказывается быстрее и в итоге Erlang — фигня, Go — rulez'ь. А уж написать про Haskell и Go в одном предложении…
Ну то есть, для ушей Go'шника есть подкаст, в котором смеются над сложностями функционального программирования.

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

Послушал. Да, был не прав. Котята действительно были) А с большинством тезисов Светланы я даже оказался солидарен.
Scala поощряет передачу сообщений через каналы. Scala поддерживает типы маркированных объединений (tagged union)

о_0 ??

Scala поддерживает типы маркированных объединений (tagged union)

Здесь говорится о ADT в Scala, сам по себе case class можно считать типом произведением, набор case class'ов унаследованый от общего sealed предка — тип сумма (который также называется tagged union).


Scala поощряет передачу сообщений через каналы.

А вот здесь я и сам в замешательстве.

fiveSeconds := 5 * time.Seconds

Не скомпилируется.
Second, опечатка просто. А так прекрасно скомпилируется.
Если Го вызывает столько холивара, то вещь стоящая, надо брать.

Разоритесь брать все вещи, вызывающие столько холиваров ;-)

Плохие вещи холиваров не вызывают: о)
Блог mail.ru хмм, как раз в мгу сейчас читается курс go от технопарка, почему же они не учат их rust или скале? И вообще популярность go в мире гораздо выше, я вот не могу понять такой вещи, если языки типо скалы, раста и хаскеля такие крутые, то почему на них вместе взятых написано меньше кода, чем на го, и если вы ответите, потому что люди глупые и не могут их осилить, а go могут, ну тогда у меня вопрос, зачем эти языки нужны, чтобы тешить чсв небольшой группы людей, нет если люди для себя программируют, то тут вопросов нет, но говорить, что какой-то язык цитирую «Я предпочёл бы не использовать Go в текущем виде.», особенно, когда колеги по цеху явно другого мнения несколько странно. По мне так язык нужно в первую очередь судить по тому, сколько хороших вещей на нем написано, ведь главная цель языка программирования — это внезапно создавать программы и доставлять неземное наслаждению программисту, главный кайф от работы я лично получаю, когда получается написать хороший код и go позволяет это сделать за счет простого синтаксиса, не приходится думать, а том какой из восхитительных возможностей воспользоваться, ты думаешь именно на логикой приложения и в этом по-моему и заключается главный кайф программирования. Всего хорошего.
то почему на них вместе взятых написано меньше кода, чем на го,

[mode=«troll»]Не могу удержаться [/mode]
Ну в этом смысле, лапша в го знатная, я больше про то, что больше компаний выбирает go, чем раст например, а компания скалы теперь в первую очередь поддерживает джаву. Почему-то те самые якобы идеальный языки не находят большого распространения в реальной разработке.

Я вам, наверно, открою страшную тайну, но компании выбирают технологии не по их объективным техническим преимуществам, а по их популярности. Аргументы обычно следующие:


  1. Проще найти специалистов знающих эту технологию
  2. Проще привлечь специалистов, которые хотят её изучить
  3. Проще найти решения различных проблем технологии (если вы столкнулись с проблемой, то скорее всего не вы один и скорее всего на каком-нибудь stackoverflow будет обстоятельный разбор похожей ситуации)
  4. Можно пиариться как технологически продвинутая компания.
  5. Если технология загнётся, то по инерции она проедет ещё не один год, чего хватит если не до конца проекта, то хотя бы до его очередного рефакторинга.
Статья написана в таком интересном ключе, что после нее для меня мульон страниц по С++ выглядит не таким уж и страшным, D — проще пареной репки, а Rust — все больше напоминает нативный инопланетянский интерфейс
Вот смотрю я а все эти новомодные языки, смотрю на старый добрый C++ и не понимаю, чего там людям не хватает? Любые парадигмы, на выбор, пиши как хочешь. Обобщается все. Как хочешь. главное чтобы воображения и фантазии хватало. Не ужели действительно проблема в том, что он многим кажется сложным????

А можно я ещё концепты и интерфейсы поканючу?


Интерфейсы C#/Java никак нельзя имитировать множественным виртуальным наследованием в C++ — всё гораздо сложнее, да и концепты в C++ изображать — тоже занятие для мазохистов.

Может я чего то не понимаю, чем класс с чисто виртуальными методами в С++ отличается от интерфейсов в C#, не в смысле реализации в архитектуром смысле.

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


Вы не сможете на C++ написать аналог следующего кода:


    class Base
    {
        public void Perform() { }
    }

    interface IFace
    {
        void Perform();
    }

    class Derived
        : Base, IFace
    { }

Компилятор C++ потребует реализации метода Peform в Derived.


Ну и да, реализация в архитектурном стиле — в GCC и MSVC реализация интерфейсов как классов никак специально не обрабатывается. В итоге каждый используемый интерфейс замусоривает vtable. Ваш класс реализует 10 интерфейсов? Пожалуйста, получите раздутый vtable: в случае виртуального наследования будет по +240 байт на каждый объект, если я не ошибаюсь. Кстати, C++ Builder, в отличие от GCC и MSVC, для интерфейсов генерит специальное, более легковесное представление.

Интерфейсы — этот инструмент задания(декларации) полиморфного поведения объекта во время исполнения, концепты в C++ — это инструмент декларации требований к типу передоваемому как аргумент шаблона, и их проверка на этапе компиляции. У вас до сих пор в C# есть возможность привести любой объект к любому интерфейсу(типу) и попытаться на нем чтото вызвать, компилятор вам тут не помешает.

ваш пример на С++ просто реализовывается по другому
template <typename IT>
class Base :public IT
{
public:
	void Perform() { }
};
class IFace
{
public:
	virtual void Perform() = 0;
};

class Derived :public Base<IFace>
{

};

А что касается vtable, не думаю что в C# нет ее аналога.
Интерфейсы — этот инструмент задания(декларации) полиморфного поведения объекта во время исполнения

Нет, в C# соответствие классов интерфейсам проверяется на этапе компиляции.


У вас до сих пор в C# есть возможность привести любой объект к любому интерфейсу(типу) и попытаться на нем чтото вызвать, компилятор вам тут не помешает.

dynamic_cast в C++ тоже никто не отменял


ваш пример на С++ просто реализовывается по другому

А если я хочу навесить второй интерфейс на Derived? А если я хочу, чтобы Base был POD, а интерфейс реализовывался в Derived, но использовал реализации функций Base? А что делать, если интерфейсов несколько, некоторые из которых включены по нескольку раз? CRTP не от хорошей жизни используется.


А что касается vtable, не думаю что в C# нет ее аналога.

В C# нет множественного наследования, а интерфейсы реализованы немного по-другому, поэтому вне зависимости от количества интерфейсов размер объекта будет одинаков, в C++ же он будет раздуваться с каждым интерфейсом.

А если я хочу навесить второй интерфейс на Derived? А если я хочу, чтобы Base был POD, а интерфейс реализовывался в Derived, но использовал реализации функций Base? А что делать, если интерфейсов несколько, некоторые из которых включены по нескольку раз? CRTP не от хорошей жизни используется.

При желании все эти требования можно реализовать, в С++11 есть using, есть виртуальное наследование(для случаев ромбовидного наследования), в конце концов наследование не панацея, есть икапсуляция.
Изначально я хотел сказать, что в C++ достаточно инструментов для реализации любых прихотей архитектора. Даже на этом этапе его развития.
В дополнение скажу, сейчас мне приходится писать практически в равных частях код на C++ и на C#, и если в C++ мне чаще всего не хватает возможностей и удобств которые дает .NET фареймвок, то в С# мне часто не хватает именно особенностей C++ как языка.
Без чего в плюсах жить реально тяжело — это без Linq. Так и чешутся руки писать Where().Select().Order() etc… Ждем ренжес :)
При желании все эти требования можно реализовать, в С++11 есть using, есть виртуальное наследование(для случаев ромбовидного наследования), в конце концов наследование не панацея, есть икапсуляция.

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


Без чего в плюсах жить реально тяжело — это без Linq. Так и чешутся руки писать Where().Select().Order() etc… Ждем ренжес :)

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


order(select(where(x, ...), ...), ...)

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

boolinq как пример. Не без проблем конечно, но реализовано все множество linq над колекциями(не linq->sql или entity), в своих внутренних проектах использовал, в продуктовых решили ждать ренджи.

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

Ну да, и получатся либо объекты с огромными накладными расходами (написал выше), либо падение производительности.
От размера vtable в С++ производительность не страдает. Одна таблица создается на _тип_ (не на экземпляр) и всего лишь косвенный вызов — одна лишняя ассемблерная инструкция для любого виртуального вызова.
От размера vtable в С++ производительность не страдает. Одна таблица создается на тип (не на экземпляр) и всего лишь косвенный вызов — одна лишняя ассемблерная инструкция для любого виртуального вызова.

В случае множественного наследования C++ объект содержит несколько ссылок на vtable, по ссылке на каждый объект. Накладные расходы — потребление памяти.

Ага, огромные накладные. По 4 байта на ссылку =)

Защищать с таких позиций С№ это даже не цирк

А теперь смотрите:


  1. Интерфейсов обычно несколько. Ну, например, десяток.
  2. В случае интерфейсов используется не обычное наследование, а виртуальное — упс, получается уже не +4 байта на каждую ссылку, а +8.
  3. Весь мир уже перешёл на 64 бита.

Вот и получается, что 10 интерфейсов дают оверхед в 80 байт в 32-битном коде и 160 байт — в 64-битном. При этом в C# тот же объект будет занимать всего 32-64 байта.


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

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

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


Вот список интерфейсов для Dictionary<TKey, TValue>:


  1. IDictionary<TKey, TValue>
  2. ICollection<KeyValuePair<TKey, TValue>>
  3. IEnumerable<KeyValuePair<TKey, TValue>>
  4. IEnumerable
  5. IDictionary
  6. ICollection
  7. IReadOnlyDictionary<TKey, TValue>
  8. IReadOnlyCollection<KeyValuePair<TKey, TValue>>
  9. ISerializable
  10. IDeserializationCallback

Никакого god-класса, наоборот, SOLID во всей красе (Interface Segregation Principle).

Да, и если честно, класс имплементирующий 10 интерфейсов сильно напоминает God Class, это мало коррелирует с принципами SOLID. Я б такой класс делать не стал. Принцип единственной отвесьвенности еще никто не отменял.
Не вижу смысла в виртуальном наследовании для интерфейсов, так что +1 указатель на интерфейс.

Кроме того, миллион интерфейсов — это нетипичная парадигма для С++, а конечно, чужие парадигмы «стоят» затрат.
У шарпа достаточно своих проблем, чтобы пытаться зацепиться за такие мелочи.
Не вижу смысла в виртуальном наследовании для интерфейсов, так что +1 указатель на интерфейс.

Зря. Это может значительно сузить область применимости интерфейсов.


Кроме того, миллион интерфейсов — это нетипичная парадигма для С++, а конечно, чужие парадигмы «стоят» затрат.

Дело не в нетипичности парадигмы, а в отсутствии поддержки функционала компиляторами. Тот же C++ Builder вполне умел генерировать легковесные интерфейсы.

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

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

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


Воооот… и язык действительно сложный(читать комплексный, расширенный): в смысле имеет много всего и к этой куче каждые 4? года ещё что-то добавляется.

Да, но посмотрев пример на Go с сумированием разнотипных списков из статьи, как то начинаешь задумываться, что лучше уж так как в C++ чем так…
А все остальное — это на любителя. С++ это инструмент для долгосрочных больших проектов, в таких проекта менеджер пакетов будет скорее принсить вред, так как будет провацировать вносить лишние зависимости, а в долгосрочных проектах — это зло, иногда цикл жизни проекта больше, чем библиотеки которую можно притащить из менеджера пакетов.
иногда цикл жизни проекта больше, чем библиотеки которую можно притащить из менеджера пакетов

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

1. Иногда библиотека не нужна, а достаточно небольшого его подмножества, в таком случае велосипед в полне может иметь место.
2. Когда ты можешь, не напрягаясь, в свой проект добавить новую зависимость, в 90% случаев ты не будешь задумываться о последствиях — а последствия, например — попытка собрать тестовое angular2 приложение притаскивает около 100Mb всякой херни на лоакальный диск, в долгосрочных проетах это не допустимо. Каждая зависимость должна быть обдумана, проанализирована, и признана годной, в таком варианте, текущая реализация внесения зависимостей в C++ вполне оправдана.

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


Кстати, "100Mb всякой херни" (сорцов? бинарей?) не особо страшны, если линкер выкинет ненужное.

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

По поводу сети, С++ реализаций сетевых библиотек сейчас вагон и маленькая тележка, низкоуровневые — boost,qt,libevent, POCO. Высокоуровневые — websocketpp, restcpp(casablanсa), qt, POCO. Это только то что вспомнил и чем приходилось пользоваться. Да и в стандарте вот-вот что то примут.
Выстрелы в конечности есть в любом языке, ошибку в логике приложения можно допустить всегда. С++ в этом плане дает больше свобод нежели другие языки, но я бы не сказал что прямо на много. Да, за циклом жизни объектов выделенных на куче нужно следить самостоятельно(и то, в прошлом веке), но сборщик мусора не всегда в этом помощник, мне как то легче не становится, что объект инкапсулирующий соединение к БД, умрет когдато, после того как я его потеряю, а чтобы освободить соединение все равно надо вызвать Dispose, и чем это не ручное управление?
Указатели? да есть возможность подарваться, но можно и минимизировать работу с ними.
Зато пописав на С++ начинаешь больше задумываться именно об архитектуре приложения, почему этот объет должен жить доглго, а этот должен умирать быстро, почему этот объект владеет другим а не наоборот, итд…
Не понятно к чему вы это пишите. Все что вы написали донельзя очевидно, я же ответил на вопрос «Чего не хватает людям в С++?» и достаточно много людей с похожим мнением. И я не пишу о тех кто уже пишет на С++, а наоборот о тех кто только начинает или выбирает «не свой» язык для решения какой-либо задачи.
банить все статьи с упоминанием выстрелов в конечности

Почему/зачем?

Это ответ на вопрос, который по тематике затрагивает плохую(а так ли?) популярность С++. Если выбирать язык для проекта/обучения и услышать «Да там ты ошибёшься даже моргнуть не успеешь, отстрелишь...».

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

P.S. Я вообще юзал повсеместно умные указатели, где нужны были именно указатели и не парился, никаких утечек памяти в нескольких проектах и поэтому категарический противник фраз про огнестрелы, но латентый, никого не призываю :)
Вот смотрю я а все эти новомодные языки, смотрю на старый добрый C++ и не понимаю, чего там людям не хватает?

Дык, люди уже написали 100500 статей на тему того, чего им не хватает. Проще всего будет объяснить следующем примере: если ты пишешь на С++ и приходишь в ужас от мысли писать на "чистом С", то вот примерно в этом и дело.


Если что, я не считаю, что раст всем превосходит и готов полностью заменить C++, но информации о его преимуществах (и недостатках) хватает. Он может не нравится и/или быть неподходящим выбором, но если не приходит понимания почему он кому-то нравится, то стоит задуматься, а не наступило ли закостенение и нежелание смотреть (повторюсь: смотреть, а не применять!) на новые штуки. Если и новые стандарты плюсов так же воспринимаются, то дело плохо. (:

UFO just landed and posted this here
Статья неоднозначна с первого взгляда — автор вызывает достаточно сомнительные впечатления, описывая вещи, которые уже давно приняты в мире Go с огромным негодованием.
Конечно, у языка, как и у любого другого есть свои проблемы.

Но я бы хотел обратить внимание на бизнес ценности. Язык — это только инструмент.

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

Резюмирую, как это все работает в моем маленьком мире:
1) Если нужно закрыть проблему бизнеса вот прямо сейчас, и мне не важна производительность — я выбираю Python.
2) Если есть долгоиграющая проблема, с кучей серверов и потребляемых мощностей, и есть немного времени — я выбираю Go.

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

Пример использования чаще всего выглядит так:
1) Микросервис на Go пакуется в Docker (который на Go написан)
2) Контейнер скейлится в парке из Kubernetes (который тоже на Go написан)

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

Коротко — Rust не выстрелить, расходимся.


Как уже сказано, пока пылисты заливают на стеке вопросы — другие пилят код на ГОУ! Пилите код бл/@
На ГОУ крутится не один вебсервис в продакшене — менять не собираюсь.

Почему выбор остановился на Go, а не на D? Он же проще и решение на нем на ограничиваются микро-сервисами.

Rust не выстрелить, расходимся.

Пишу (в команде) код на расте, жизнью доволен. (:

Дык, я такого и не утверждал. Это к тому, что на расте тоже код пишут.


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

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

Пока тут идёт gosrach, люди пишут на Го миллионы строк кода, на котором работает инфраструктура всего интернета: о) И что удивительно, отсутствие женериков в Го вообще их не волнует.

А не придуманы ли женерики для тех ЯП, которым не хватает возможностей Го?

год спустя — волнует.
Жду go 2.0

Объективно Go как раз задумывался как язык с минимальным набором фич, для простоты изучения и понимания, как результат начинающим/в начале все кажется круто, когда вырастаешь все затягивается рутиной и хочется больших возможностей.
По этому вопрос вы почему выбирали Go, если эти цели были сразу очерчены — простой язык со встроенной простой работой моделью многопоточности что бы даже последний простолюдин мог написать работающий и читаемы код.
Для вас открыт весь мин, например jvm — Java, Scala, Clojure. Можете что-то другое поискать.

UFO just landed and posted this here

А чем интерфейсы в Го отличаются от дженериков в Яве?.

UFO just landed and posted this here

Вообще.


Первое, что должно быть заложено в хороший язык — возможность расширения идиом. Вот Лисп — идеальный язык :-D

Не уверен, что вы правы.

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

Но даже изначальное ориентирование на универсальность применения не гарантия для языка. Сколько крупных проектов на D вы знаете? Я уж молчу про экзотику вроде ocaml-а.

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


http://dlang.org/orgs-using-d.html

У Rust, да даже у плюсов он по вашему более человечный? :)

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

Он более предсказуемый и содержит меньше спецсимволов. http://strombergers.com/python/


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

Внешние изменения в тройке питона не столь глобальны, хотя и важны. На сколько я помню, самая большая свалка была вокруг поддержки уникода. В остальном, что-то добавили, что -то убрали, для питона это обычная история. Язык живет и развивается.
Sign up to leave a comment.