LINUX.ORG.RU

Опубликован почти окончательный драфт генериков в Go

 


0

10

За подробностями в Go-блог (blog.golang.org/why-generics), там есть ссылка на собственно драфт.

Генерики семантически будут наподобие шаблонов C++, т.е. не boxed (как в Java), а value: компилятор будет генерировать копии с конкретными типами.

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

В релизе появится всё это не скоро, в Go 2, срок выхода которого неизвестен. Go 1.13 появится на днях, 1.14 — в ноябре, к десятилетию первого публичного бета-релиза.

★★★★
Ответ на: комментарий от Siborgium

Что же тогда, с точки зрения языка, возвращает такая функция?

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

Почему не хватило жабовских генериков

User-defined генерики не планировались с самого начала, т.к. они противоречат религии простоты, которую проповедует Go. Надо отметить, что встроенные типы вроде array, slice, map и channel — обобщённые, т.е. значительная доля применений генериков покрыта.

hbee ★★★★
() автор топика
Ответ на: комментарий от hbee

гетерогенный список значений не кортеж

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

элегантности обработки значений

a, err := foo()
if err == nil {
    b, err := bar(a)
    if err == nil {
        ...
    }
}

Элегантность/10, скручиваем стек руками. Для чего изобретали исключения – непонятно. Что еще более важно, необходимость проверки err на nil ненамного выше, чем необходимость проверки errno в Сишке. Ее почти нет. Кстати, Раст, также отказавшийся от исключений, предлагает map, and_then и прочие функциональные прелести, сильно снижая длину необходимого кода и убирая бойлерплейт. При этом еще и принуждает проверять на ошибки, либо явно отказываться от проверки. И если где-то что-то ломается, можно просто grep unwrap, с большой вероятностью найдя источник ошибки.

встроенные типы […] – обобщённые

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

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

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

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Для чего изобретали исключения – непонятно

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

В Go, кстати, есть механизм исключений и их ловли (panic/recover), но применяется он не везде, а только где нельзя обойтись без.

hbee ★★★★
() автор топика
Ответ на: комментарий от Siborgium

Проблема с Go вовсе не в том, что он примитивнее аналогов. Проблема в нежелании адептов Go признать это

Какая-то надуманная проблема. Мы же не в академическом или артистическом мире, где непременно требуется какое-то внешнее признание и одобрение. Программирование — мир практиков. Если конкретный язык практичнее для конкретной задачи, то неважно, как его оценивают другие — простой, примитивный или ещё какой.

hbee ★★★★
() автор топика
Ответ на: комментарий от hbee

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

Откуда эта чушь пошла?

Java, C#, Python, Scala, Kotlin и т.д. и т.п.

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

anonymous
()
Ответ на: комментарий от hbee

Больше вреда, чем пользы

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

errno_t err1 = foo (&a);
if (err1) {
    goto label1;
}
errno_t err2 = bar (&b);
if (err2) {
    goto label2;
}

...

label1:
recover1 ();
goto end;

label2:
recover2 ();
goto end;

end:
return;

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

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

В языке же с исключениями это просто

try {
    foo (&a);
    bar (&b);
}
catch (exception1_t e) {
...
}
catch (exception2_t e) {
...
}

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

В Go же только

if 
    if
        if
            if
Siborgium ★★★★★
()
Ответ на: комментарий от hbee

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

Ну и, как замечено выше, такое заявление не соответствует истине. Действительно, многие проекты на С++ избегали и избегают использования исключений, так как исключения в С++ ощутимо медленнее аналогов в других языках. Проблема частично решилась с взрослением языка, когда сформировались правила хорошего тона использования исключений, а до конца будет починена в С++20 (если пропозал Саттера «Lightweight exceptions» будет принят, не знаю его статуса на данный момент, но комитету он понравился).

Siborgium ★★★★★
()
Ответ на: комментарий от hbee

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

А ещё, если пулять как в Си через указатель в аргументах, то в Go получится намного медленнее из-за возни связанной со сборщиком мусора.

package main

import "testing"

func x1(a int) (ax int) {
	return a + 1
}

func p1(a *int) {
	(*a) += 1
}

func x2(a, b int) (ax, bx int) {
	return a + 1, b + 2
}

func p2(a, b *int) {
	(*a) += 1
	(*b) += 2
}

func x3(a, b, c int) (ax, bx, cx int) {
	return a + 1, b + 2, c + 3
}

func p3(a, b, c *int) {
	(*a) += 1
	(*b) += 2
	(*c) += 3
}

func x4(a, b, c, d int) (ax, bx, cx, dx int) {
	return a + 1, b + 2, c + 3, d + 4
}

func p4(a, b, c, d *int) {
	(*a) += 1
	(*b) += 2
	(*c) += 3
	(*d) += 4
}

var ga, gb, gc, gd int

func Benchmark(b *testing.B) {

	//

	b.Run("reply-1", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			ga = x1(i)
		}
		b.ReportAllocs()
	})

	b.Run("pointer-1", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			p1(&ga)
		}
		b.ReportAllocs()
	})

	//

	b.Run("reply-2", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			ga, gb = x2(i, i)
		}
		b.ReportAllocs()
	})

	b.Run("pointer-2", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			p2(&ga, &gb)
		}
		b.ReportAllocs()
	})

	//

	b.Run("reply-3", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			ga, gb, gc = x3(i, i, i)
		}
		b.ReportAllocs()
	})

	b.Run("pointer-3", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			p3(&ga, &gb, &gc)
		}
		b.ReportAllocs()
	})

	//

	b.Run("reply-4", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			ga, gb, gc, gd = x4(i, i, i, i)
		}
		b.ReportAllocs()
	})

	b.Run("pointer-4", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			p4(&ga, &gb, &gc, &gd)
		}
		b.ReportAllocs()
	})

}
Benchmark/reply-1-4             2000000000               0.39 ns/op            0 B/op          0 allocs/op
Benchmark/pointer-1-4           1000000000               2.13 ns/op            0 B/op          0 allocs/op
Benchmark/reply-2-4             2000000000               0.77 ns/op            0 B/op          0 allocs/op
Benchmark/pointer-2-4           1000000000               2.10 ns/op            0 B/op          0 allocs/op
Benchmark/reply-3-4             2000000000               1.17 ns/op            0 B/op          0 allocs/op
Benchmark/pointer-3-4           2000000000               2.00 ns/op            0 B/op          0 allocs/op
Benchmark/reply-4-4             2000000000               1.60 ns/op            0 B/op          0 allocs/op
Benchmark/pointer-4-4           1000000000               2.13 ns/op            0 B/op          0 allocs/op
anonymous
()
Ответ на: комментарий от Siborgium

Проблема с Go вовсе не в том, что он примитивнее аналогов. Проблема в нежелании адептов Go признать это.

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

bread
()
Ответ на: комментарий от anonymous

А ещё, если пулять как в Си через указатель в аргументах, то в Go получится намного медленнее из-за возни связанной со сборщиком мусора.

Спасибо, ценное замечание. В этом, кстати, таится ресурс ускорения Go: улучшить т.н. escape analysis, увеличить долю размещения значений на стеке, а не в куче.

hbee ★★★★
() автор топика
Ответ на: комментарий от Siborgium

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

Проблема деградации производительности не главная. Гораздо хуже, что исключения снижают качество обработки ошибок. Грубо говоря, обработка теряет в тонкости, детальности, уместности, потому что в блоки try включаются большие куски логики (а иначе, если городить try/catch вокруг каждого вызова функции, живые позавидуют мёртвым). Разработчик поддаётся искушению «срезать углы», делая только catch(std::exception) и catch(...). В самых клинических случаях ошибка только логируется в надежде разобраться позже.

Go же поощряет обработку максимально близко к месту возникновения ошибки. Пресловутый boilerplate code (if err != nil { do something }) сделан настолько компактным, насколько возможно. Исключения panic и их ловля recover намеренно устроены так, чтобы их использование не было чересчур комфортным и кратким и не создавало соблазна.

hbee ★★★★
() автор топика
Ответ на: комментарий от hbee

Очень бы не помешали примеры. Вот, мол, ваше говно на исключениях в C++/Java/C#/Scala/Kotlin/Ruby/Python и иже с ними, а вот как няшно это же самое выглядит на Go с кодами ошибок.

eao197 ★★★★★
()
Ответ на: комментарий от hbee

if err != nil { do something } побуждает сделать

if err != nil {
    fmt.Printf("Shit happened")
    return nil, err
}

и хорошо, если Printf еще будет.

Более того, отдельные сверхразумы додумались до сделанного вручную таким образом стек трейса, ведь, очевидно, дебаггеры – не Go way.

Siborgium ★★★★★
()
Ответ на: комментарий от eao197

Берём список «top 10 C++ projects on github», берём первый из них — Tensorflow (>130 тыс. stars). И что же видим в первом же попавшемся файле tensorflow/core/util/mkl_util.h? Всё то же самое — гигантский блок try, в котором делается невесть что, и примитивный блок catch — «логируй всё, потом разберёмся!». О кошмарном стиле форматирования уже не говорю, за отступы в два пробела надо заставлять работать на сорокадюймовом мониторе))).

  } catch (mkldnn::error& e) {
    string error_msg = "Status: " + std::to_string(e.status) +
                       ", message: " + string(e.message) + ", in file " +
                       string(__FILE__) + ":" + std::to_string(__LINE__);
    LOG(FATAL) << "Operation received an exception: " << error_msg;
  }
hbee ★★★★
() автор топика
Ответ на: комментарий от Siborgium

отдельные сверхразумы додумались до сделанного вручную таким образом стек трейса, ведь, очевидно, дебаггеры – не Go way

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

hbee ★★★★
() автор топика
Ответ на: комментарий от Siborgium

хорошо, если Printf еще будет

Логирование ошибки — уже обработка (когда совсем ничего сделать нельзя, так как потерян контекст, как в вышеприведённом примере с C++). Хороший стиль — исправить, если возможно, на месте, если нет — передать вверх, добавив контекст.

hbee ★★★★
() автор топика
Ответ на: комментарий от hbee

Всё то же самое — гигантский блок try, в котором делается невесть что, и примитивный блок catch — «логируй всё, потом разберёмся!».

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

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Ну какой ещё труд на лоре)))). А пример ещё будет, я тоже, знаете ли, не сижу все время тут. Спешить вроде некуда.

hbee ★★★★
() автор топика
Ответ на: комментарий от hbee

Ну какой ещё труд на лоре))))

LOR и так стремительно преврашают в УГ персонажи вроде царя, метапрога и смайлодауна. Должны же здесь оставаться люди, с которыми можно предметно поговорить.

Жалоб на исключения много, но, обычно, когда доходишь до конкретики, то выясняется, что нормальных примеров-то и нет. По крайней мере если разговор идет об императивных языках, вроде Go, Java или C++.

А пример ещё будет

OK. Спешить некуда.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Что значит «то же самое»? Реализации tensorflow на Go нет. А тьма примеров хорошей обработки наблюдается прямо в стандартной библиотеке, которая написана весьма ясно и служит примером для подражания. Например:

// ReadDir reads the directory named by dirname and returns
// a list of directory entries sorted by filename.
func ReadDir(dirname string) ([]os.FileInfo, error) {
	f, err := os.Open(dirname)
	if err != nil {
		return nil, err
	}
	list, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return nil, err
	}
	sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
	return list, nil
}

Лучше ли было бы обработать обе эти ошибки в гипотетическом блоке catch? Нет, так как природа у них разная (и при возможном в будущем изменении типа ошибки ничего менять у клиента не надо). Читаемость кода отличная, обработка стопроцентная, ничего не пропущено.

hbee ★★★★
() автор топика
Ответ на: комментарий от hbee

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

И в языке с исключениями и RAII вы бы записали что-то вроде:

vector<FileInfo> ReadDir(Path dirname) {
   auto list = os.ReadDir(os.Opendir(dirname));
   sort.Slice(list, func(usize i, usize j) bool { return list[i].Name() < list[j].Name() });
   return list;
}

В точности все тоже самое, но в 2.5 раза короче.

eao197 ★★★★★
()
Ответ на: комментарий от hbee

резвычайно важна в Go для достижения элегантности обработки ошибок.

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

Zenom ★★★
()
Ответ на: комментарий от hbee

Пресловутый boilerplate code (if err != nil { do something }) сделан настолько компактным, насколько возможно.

Это неправда.

file := os.Open(fileName) or err {
    return nil, errors.Wrap(err, "open config file")
}

против

file, err := os.Open(fileName)
if err != nil {
    return nil, errors.Wrap(err, "open config file")
}

В альтернативном варианте лучше:

  1. Гарантируется типобезопасность
  2. Пространство имён не загрязняется всякими err
  3. Компактнее

Абсолютно аналогично можно было бы ввести понятие nilable типов с аналогичной обработкой (or без err), таким образом полностью исключив класс NPD-ошибок. Для удобства, правда, пришлось бы вводить if и switch операторы для инициализаций:

val := if a < b {
    yield a
} else {
    yield b
}

потому как иначе var a map[string]string вынужден был бы стать var a ?map[string]string с обязательной проверкой

if a != nil {
    
}

Параллельно бы это упростило муторный код для работы с мапами:

a := map[string]string{}
// …
b := a["value"] or {
    yield "default"
}

вместо

a := map[string]string{}
// …
b, ok := a["value"]
if !ok {
    b = "default"
}

PS Любителям исключений: у вас с головой всё в порядке? Из раза в раз получается, что Go-бекенды делаются в два раза быстрее джавовых и при этом в те же два раза быстрее и жрут раз в 8 меньше памяти и изначального намного стабильнее блогодаря как раз явной обработке ошибок. Я в курсе, что для многих задач исключения вполне себе хороший способ, но только не для бекендов.

Joe_Bishop
()
Ответ на: комментарий от Joe_Bishop

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

Ну да. Дело же в исключениях. Не в байт-коде, не в jit-е, не в особенностях GC (и отсутствия навыков/желания его тюнить), не в жирных java-вских структурах данных, не в boxing/unboxing, не в чрезмерном увлечении run-time рефлексией… А в обработке ошибок.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

не в особенностях GC (и отсутствия навыков/желания его тюнить)

Ну были у нас персонажи, которые свои жабоподелия пытались дотюнить до уровня Go-приложенек дёрганьем настроек. Чота ничего у них не получилось: исправляли одно, ухудшали другое.

не в жирных java-вских структурах данных, не в boxing/unboxing, не в чрезмерном увлечении run-time рефлексией… А в обработке ошибок.

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

Joe_Bishop
()
Ответ на: комментарий от Joe_Bishop

Ну были у нас персонажи, которые свои жабоподелия пытались дотюнить до уровня Go-приложенек дёрганьем настроек. Чота ничего у них не получилось: исправляли одно, ухудшали другое.

Самое смешное, что GC в Go именно так и работает: оптимизируем продолжительность пауз ценой всего остального. Подробности: https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e

Zenom ★★★
()
Ответ на: комментарий от Joe_Bishop

неправда

file := os.Open(fileName) or err {
    return nil, errors.Wrap(err, "open config file")
}

Похоже на это предложение: https://github.com/golang/go/issues/32848

Его зарубили за неестественный, «non-Goish» синтаксис, от чего страдает читаемость. Не всегда более краткий код читабельнее.

hbee ★★★★
() автор топика
Ответ на: комментарий от Zenom

Самое смешное, что GC в Go именно так и работает: оптимизируем продолжительность пауз ценой всего остального. Подробности: https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e

Такая, %%ядь, проблема )

У тех, у которых такое является проблемой, Жаба умудряется обгонять в тестах C++.

Joe_Bishop
()
Ответ на: комментарий от Joe_Bishop

Как раз это – Goish
file := os.Open(fileName) or err

что будет в file в случае ошибки ? И конструкция весьма сложночитаема, например не получается моментально определить, в чем синтаксический смысл строки, в присваивании или условии. Более того, получается что часть переменных находятся в разных областях видимости, при этом создаются в одной строке кода. Существующий синтаксис все же лучше(который if err:=f(x); err != nil{}). Позволят точно дать понять, что это проверка и что создаваемые переменные не используются дальше по коду.

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

Dred ★★★★★
()
Ответ на: комментарий от Dred

что будет в file в случае ошибки ?

обязательный выход из скоупа в котором был вызов os.Open либо возврат переменной типа *of.File (которая не равна nil, если что) из обработчика c помощью yield os.Stdin

идея развита из https://vlang.io/docs#option

в общем и целом конструкция

file := os.Open(fileName) or err {
    return errors.Wrapf(err, "open config file")
}
A(file)

эквивалентна растовому

match File::open(filename) {
    Ok(file) => {
         A(file)
    }
    Err(err) => {
        return ...
    }
}

При этом позволяет избежать елочки

Joe_Bishop
()
Ответ на: комментарий от Joe_Bishop

эквивалентна растовому

раст - не го

идея развита из

это тоже не го, более того, то что оно там - не значит что оно хорошо

обязательный выход из скоупа

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

либо

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

обработчика c помощью yield os.Stdin

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

которая не равна nil, если что

но может быть, ссылка вполне может быть nil, более того, это тоже удобно и показательно.

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

Dred ★★★★★
()
Последнее исправление: Dred (всего исправлений: 1)
Ответ на: комментарий от Joe_Bishop

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

Примеры фстудию

ya-betmen ★★★★★
()
Ответ на: комментарий от Dred

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

yield make(map[string]string)

? Так сложно понять?

выход куда ?

return? break? continue? panic?

но на деле крайне нечитабильно и более того, дебажать это сложнее.

чем это нечитабельнее того же if err := ....; err != nil {?

дебажать это сложнее.

почему?

Joe_Bishop
()
Ответ на: комментарий от Joe_Bishop

почему?

нет явной последовательности

return? break? continue? panic?

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

чем это нечитабельнее того же

я писал выше, прочти внимательно.

Так сложно понять?

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

Dred ★★★★★
()
Ответ на: комментарий от RazrFalcon

Есть же ?.

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

Joe_Bishop
()
Ответ на: комментарий от RazrFalcon

Есть же ?.

Такие пропозалы в Go-сообществе сбиваются на взлёте самонаводящейся ракетой Cryptic :).

hbee ★★★★
() автор топика
Ответ на: комментарий от Joe_Bishop

Как образом теряется контекст? Это просто сахар на лапшой.

RazrFalcon ★★★★★
()
Ответ на: комментарий от Joe_Bishop

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

А вы могли бы на примерах кода проиллюстрировать свою мысль? Мол, вот так хорошо, а вот так с ? – уже плохо.

eao197 ★★★★★
()
Ответ на: комментарий от Dred

yield нет в Go. Про какую реалиацию ты сейчас говоришь ?

Нет, это совпадение. Он возвращает значение наружу из шкоупа. Желателен для избегания nilable-значений и для возвращения значений по-умолчанию при обработках нештатных ситуаций. Можно использовать что-то другое, например continue with <value> или with <value>.

Например

file := os.Open(fileName) or err {
    log.Warning().Err(err).Msgf("failed to open input file, using stdin instead")
    yield os.Stdin
}

попытается открыть файл с названием fileName и сохранить в переменной file. Если не получилось, сообщит об этом в логе и использует в качестве значения file os.Stdin. Чуть сложно, но гарантирует типобезопасность. Что немаловажно, имеет императивный дух, и не требует адских нечитаемых ёлочек.

Joe_Bishop
()
Ответ на: комментарий от eao197

А вы могли бы на примерах кода проиллюстрировать свою мысль? Мол, вот так хорошо, а вот так с ? – уже плохо.

Лениво. На пальцах так: есть какая-то абстракция Transport с методом Transport, есть её реализации. Метод может зваться в разных местах. Если он будет давать ошибку, то пропуская его с помощью ? мы будем знать, что в какой-то из реализаций произошла ошибка без указания того, какой вызов метод вызвал ошибку – это если мы аннотировали её там. А если не аннотировали, то вообще может показываться какая-то сетевая/дисковая/… низкоуровневая ошибка. Это самый простой случай. В реальности в обработке ошибки могут, кроме аннотаций, случаться всякие восстановления целостности данных, запуски асинхронных задач и т.п.

Joe_Bishop
()
Ответ на: комментарий от Joe_Bishop

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

eao197 ★★★★★
()
Ответ на: комментарий от Joe_Bishop

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

не требует адских нечитаемых ёлочек

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

Dred ★★★★★
()
Ответ на: комментарий от Dred

А если разные ошибки (EOF, ItemNotFound и тд)

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

type ReadResult oneof {
    Length int64
    EOF    int64
    Error  error
}

type Reader interface {
    Read(p []byte) ReadResult
}

// …

for {
    switch v := f.Read(p) {
    case io.Length:
        buf.Write(p[:int(v)]) or err {
            return nil, errors.Wrap(err, "copying from %s into a memory buffer", f.Name())
        }
    case io.EOF:
        buf.Write(p[:int(v)]) or err {
            return nil, errors.Wrap(err, "copying from %s into a memory buffer", f.Name())
        }
        return buf.Bytes(), nil
    case io.Error:
        return nil, errors.Wrapf(err, "reading %s", f.Name())
    }
}

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

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

Joe_Bishop
()
Ответ на: комментарий от hbee

ресурс ускорения Go: улучшить т.н. escape analysis, увеличить долю размещения значений на стеке, а не в куче

И вот в новом 1.13 (вышел пока только RC1):

The compiler has a new implementation of escape analysis that is more precise. For most Go code should be an improvement (in other words, more Go variables and expressions allocated on the stack instead of heap).

https://tip.golang.org/doc/go1.13

Процесс идёт.

hbee ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.