Re: Арифметика констант в языке Go
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.05.17 07:20
Оценка: 19 (2) +1
Здравствуйте, Aleх, Вы писали:

Ну, в C тоже полного единообразия нет. Целочисленная константа имеет тип, минимальный из того, во что она влезает (в стандарте определён порядок — signed int, unsigned int, signed long, unsigned long...; для oct/hex пробуются оба signed/unsigned, для десятичных — только один). Другой вопрос, что конверсию там не отсекают для констант, совершенно нормально передать -1 в unsigned, несмотря на то, что C не гарантирует использование дополнительного кода.

A>Авторы языка посчитали, что с правилами приведения типов для чисел в языке Си имеются большие проблемы. Им показалось, что одной из существенных проблем в Си являются ошибки, связанные с неправильным использованием арифметических операторов для чисел разных типов. В результате они придумали нечто: https://blog.golang.org/constants Суть в том, что для констант действуют одни правила, а для переменных другие.


A>Это приводит к следующим эффектам:

A>
A>var a = -1;
A>fmt.Println(uint32(a)); // OK
A>const b = -1;
A>fmt.Println(uint32(b)); constant -1 overflows uint32
A>

A>Почему для переменных переполнения разрешено, а для констант нет? (Потому что в одном случае в момент компиляции можно проверить, а в другом нет. Серьезно?)

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

A>Или таким:

A>
A>var a = 9223372036854775296 + 1.0;
A>const b = 9223372036854775296 + 1.0;
A>fmt.Println(uint64(a), " ", uint64(b)); // 9223372036854775808   9223372036854775297
A>


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

A>Сравним с C++:

A>
A>auto a = 9223372036854775296 + 1.0;
A>constexpr auto b = 9223372036854775296 + 1.0;
A>std::cout << uint64_t(a) << " " << uint64_t(b); // 9223372036854775808 9223372036854775808
A>


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

A>Мне такой подход кажется нелогичным из-за отсутствия единообразия в правилах вычисления переменных и констант. В подходе С++ нужно понять как работает приведение типов и арифметика. Константность выражения на способ вычисления не влияет. В Go, как и в любом другом языке с конечнозначными числами, от необходимости понимания арифметики уйти не получится, но в отличие от С++, придется помнить разные правила для констант и переменных.


Да

A>Выглядит так, что хотели обезопасить программистов от "сложных" правил арифметики чисел, а в итоге только всё усложнили и сделали менее удобным. Это не та область программирования, в которой нужно что-то пытаться упростить. Кажущаяся сложность арифметики чисел является неустранимой. Невозможно писать качественный код, не зная и не думая об этом.


Go вообще представляет в этом плане собой дикую смесь продвинутых решений и самого дикого мракобесия.
  • Заточка на дополнительный код для отрицательных и фиксированные размеры типов — ok, не он первый (Java, C#, Javascript...); вообще, я бы сказал, что поскольку других платформ нет и долго не будет, дополнительный код можно ставить умолчанием.
  • Требование явной конверсии между числовыми типами — в плюс (когда оно таки требуется — по примеру выше, тут грубая недоработка).
  • Принудительность арифметики с заворотом (остаются младшие N бит) — в принципе тоже на пользу (особенно, например, по сравнению с такими
    Автор: Кодт
    Дата: 18.06.14
    ситуациями; хотя я бы категорически предпочёл более гибкое синтаксическое регулирование), но как минимум надо было в стандартную библиотеку добавить функции контроля (типа таких).
  • Мелочи, но вкусные, типа "битовые сдвиги не усекают ширину сдвига до ширины слова" (хотя тоже желательно бы видеть в библиотеке оптимизированные машинные операции, для которых обеспечить адекватную ширину — проблема программиста).
  • И вообще, группа ситуаций, где C говорит про undefined behavior или implementation defined, получили однозначное решение.

    С другой стороны, древности и глупости
  • Тупейшие правила "неиспользованная переменная, импорт => ошибка компиляции" (автогенерация пошла лесом)
  • Отказ от исключений и дженериков
  • Нормирование экспорта по регистру первого символа
  • Отсутствие private в классах
  • Синтаксические неровности типа "в import надо кавычки на пакет, а в package — нет"
  • Совсем тупые древности типа префикса 0 для восьмеричных констант
  • Ляпы в работе с каналами
  • Странная логика понятия "новый тип, такой же, как старый" (алиасы нельзя вводить; в Ada это решили явным new в type X is new Y) (UPD: в последних версиях добавили)
  • При компиляции — отказ от стандартных фреймворков и "закат солнца вручную" со своим кодогенератором и оптимизатором, заведомо более слабыми, чем майнстрим вроде LLVM (несмотря на то, что сишный код интерфейса к ОС никуда не девается).
  • Неизменяемая заточка GC на минимум задержек, хотя далеко не всем задачам это нужно.

    можно продолжать.

    Вместе с невменяемой "харизмой" авторов это приводит к тому, что всем критикам говорят "не нравится — чемодан, вокзал, Java" (или другой язык, подставить по вкусу), а в пользователях — к формированию клуба фанатиков вместо трезвых пользователей (например, на DOU два таких уже задолбали всех).
  • The God is real, unless declared integer.
    Отредактировано 14.07.2019 6:09 netch80 . Предыдущая версия . Еще …
    Отредактировано 17.05.2017 13:53 netch80 . Предыдущая версия .
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.