Ваш язык программирования — отстой

https://wiki.theory.org/YourLanguageSucks
  • Перевод
1 Почему JavaScript отстой
• 1.1 Плохая конструкция
• 1.2 Система типов
• 1.3 Плохие функции
• 1.4 Отсутствующие функции
• 1.5 DOM
2 Почему Lua отстой
3 Почему PHP отстой
• 3.1 Исправлено в поддерживаемых в настоящее время версиях
4 Почему Perl 5 отстой
5 Почему Python отстой
• 5.1 Исправлено в Python 3
6 Почему Ruby отстой
7 Почему Flex/ActionScript отстой
8 Почему скриптовые языки отстой
9 Почему C отстой
10 Почему C++ отстой
11 Почему .NET отстой
12 Почему C# отстой
13 Почему VB.NET отстой
15 Почему Objective-C отстой
16 Почему Java отстой
• 16.1 Синтаксис
• 16.2 Исправлено в Java 7 (2011)
• 16.3 Модель
• 16.4 Библиотека
• 16.5 Обсуждение
17 Почему Backbase отстой
18 Почему XML отстой
19 Почему отстой XSLT/XPath
20 Почему CSS отстой
• 20.1 Исправлено в CSS3
21 Почему Scala отстой
22 Почему Haskell отстой
23 Почему Closure отстой
24 Почему Go отстой
• 24.1 Базовые средства программирования (базовый язык)
• 24.2 Взаимосовместимость
• 24.3 Стандартная библиотека
• 24.4 Набор инструментальных средств
• 24.5 Сообщество
25 Почему Rust отстой
• 25.1 Безопасность
• 25.2 Синтаксис
• 25.3 Конструкция API и система типов
• 25.4 Сообщество
• 25.5 Набор инструментальных средств

Почему JavaScript отстой


Учтите, что некоторые положения относятся не к самому JavaScript, а к программным интерфейсам веб-приложений (https://developer.mozilla.org/en/docs/Web/API).

Плохая конструкция

• Каждый скрипт исполняется в едином глобальном пространстве имён, доступ в которое возможен в браузерах с оконным объектом.
• Camel-регистр никуда не годится:

XMLHttpRequest
HTMLHRElement


• Автоматическое преобразование типа между строками и числами в сочетании с перегруженным "+" означает конкатенацию и сложение. Это порождает очень неожиданные явления, если вы непредумышленно преобразуете число в строку:

var i = 1;
//  некоторый код
i = i + ""; //  ой!
//  ещё какой-то код
i + 1;  //  преобразует в строку "11"
i - 1;  //  преобразует в число 0

• Автоматическое преобразование типа для функции + также ведёт к наглядному явлению, что += 1 отличается от оператора ++. То же происходит при сортировке массива:

var j = "1";
j++; // j приобретает значение 2

var k = "1";
k += 1; // k приобретает значение "11"

[1,5,20,10].sort() // [1, 10, 20, 5]

• Оператор var использует область действия функции, а не область действия блока, что интуитивно совершенно непонятно. Вместо этого хочется использовать оператор let.

Система типов

• JavaScript выстраивает мир в точную иерархию прототипов с Объектом наверху. На самом деле элементы не вписываются в точную иерархию.

• Невозможно что-то унаследовать от массива или других встроенных объектов. Синтаксис наследования через прототипы также представляется весьма загадочным и необычным. (Исправлено в ES6).

• Что не устраивает в наследовании через прототипы в JavaScript: функции, заданные в прототипе, не могут получить доступ к аргументам и локальным переменным в конструкторе, означая, что такие «открытые методы» не могут получить доступ к «приватным полям». Для какой-то функции оснований стать методом мало или нет вообще, если метод не может получить доступ к приватным полям. (Исправлено в ES6 через символы).

• JavaScript не поддерживает хэши или словари. Можно рассматривать такие объекты, однако Объекты наследуют свойства __proto__, что создаёт проблемы. (Используйте Object.create(null) в ES5 или Map в ES6).

• Аргумент не является массивом. Можно преобразовать его в таковой, используя срез (или Array.from в ES6):

var args = Array.prototype.slice.call(arguments);

(Аргументы в итоге будут устаревшими).

• Числовой тип имеет проблемы с точностью.

0.1 + 0.2 === 0.30000000000000004;

Проблема не в ожидаемом результате, а в выборе использования числа с плавающей точкой для представления чисел, и это является отложенным выбором разработчика языка. См. http://www.math.umd.edu/~jkolesar/mait613/floating_point_math.pdf.

• NaN не является обозначением числа, а само по себе является числом.
typeof NaN === "number"
// Чтобы сделать ситуацию ещё более трудной, NaN не равно самому себе
NaN != NaN
NaN !== NaN

// Проверяется, является ли "х" числом "NaN".
x !== x
// Это - правильный способ тестирования
isNaN(x)

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

• Нуль (null) не является экземпляром Объекта, но typeof null === 'object'.

Плохие функции

(Можно обойти многие из этих плохих функций, используя http://www.jslint.com/)

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

• JavaScript унаследовал непонятный и проблемный синтаксис регулярных выражений у Perl.

• Ключевое слово «this» («это») является неоднозначным, сбивает с толку и вводит в заблуждение:

// "This" как локальная ссылка на объект в некотором методе
object.property = function foo() {
   return this; // "This" является объектом, к которому присоединена функция (метод)
}

// "This" как глобальный объект
var functionVariable = function foo() {
   return this; // "This" является окном
}

// "This" как новый объект
function ExampleObject() {
  this.someNewProperty = bar; // "This" указывает на новый объект
  this.confusing = true;
}

// "This" как локально изменяемая ссылка

function doSomething(somethingHandler, args) {
   somethingHandler.apply(this, args); // Здесь "this" будет тем, что мы "обычно" ожидаем
   this.foo = bar; // "This" было изменено вызовом "применить"
   var that = this;

   // Но это только начало, потому что смысл "this" может измениться три раза в одной функции
   someVar.onEvent = function () {
        that.confusing = true;
        // Здесь "this" относилось бы к someVar
   }
}

• Вставка точки с запятой

// "This" возвращается неопределённым
return
{
  a: 5
};

• Объекты и операторы, а также метки имеют очень схожий синтаксис. Пример выше на самом деле возвращался неопределённым, затем формируя некоторый оператор. Этот пример в действительности вызывает ошибку синтаксиса.

// "This" возвращается неопределённым
return
{
  'a': 5
};

• Подразумеваемые глобальные объекты:

function bar() {
  // М-да, я не указал ключевое слово var, теперь у меня есть глобальная переменная
  foo = 5;
}

(Это может быть исправлено при использовании директивы «use strict» («строгий режим») в ES5.)

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

0 == ""
0 == "0"
0 == " \t\r\n "
"0" == false
null == undefined

""    != "0"
false != "false"
false != undefined
false != null

• Недостаток типизированных обёрток:

new Function("x", "y", "return x + y");
new Array(1, 2, 3, 4, 5);
new Object({"a": 5});
new Boolean(true);


• parseInt имеет действительно странное поведение по умолчанию, так что в общем случае необходимо добавлять его, если требуется, чтобы ваше основание логарифма было 10:

parseInt("72", 10);

Можно использовать Number('72') для преобразования в число.

• Оператор «with» (не рекомендуемый) имеет тот недостаток, что он подвержен ошибкам.

with (obj) {
  foo = 1;
  bar = 2;
}

• Оператор «for in» участвует в цикле через элементы, унаследованные через цепь прототипов, поэтому его в общем случае необходимо включить в длинный вызов к object.hasOwnProperty(name) или использовать Object.keys(...).forEach(...).

for (var name in object) {
  if (object.hasOwnProperty(name)) {
    /* ... */
  }
}
// Или
Object.keys(object).forEach(function() { ... });

• Там нет числовых массивов, имеются только объекты со свойствами, и эти свойства называются по текстовым строкам; как следствие петля «for in» проваливается при действиях на псевдочисловых массивах, поскольку итерационной переменной является фактически строка, а не число (это делает добавление целого числа трудным делом, т.к. необходимо вручную выполнять функцию parseInt с итерационной переменной при каждой итерации).

var n = 0;
for (var i in [3, 'hello world', false, 1.5]) {
  i = parseInt(i); // выход является неправильным без этой громоздкой строки
  alert(i + n);
}
// Или
[3, 'hello world', false, 1.5].map(Number).forEach(function() { alert(i + n) });

• Имеется также много устаревших (нерекомендуемых) функций (см. https://developer.mozilla.org/en/JavaScript/Reference/Deprecated_Features), таких как getYear и setYear на объектах Date.

Отсутствующие функции

• Потребовалось ждать ES6, чтобы обеспечить неизменяемость. Этот оператор не работает для наиболее важных типов данных JavaScript — объектов, для которых приходится использовать Object.freeze(...).

// Это хорошо работает для чисел и строк
const pi = 3.14159265358;
const msg = "Hello World";

// Это не работает для объектов
const bar = {"a": 5, "b": 6};
const foo = [1, 2, 3, 4, 5];

// Также довольно трудно сделать ваши параметры постоянными
const func = function() {
  const x = arguments[0], y = arguments[1];

  return x + y;
};

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

ES6
x -> x * x

• Учитывая важность экспоненциальности в математике, Math.pow должен быть на самом деле инфиксным оператором, таким как **, а не функцией. (Исправлено в ES6 как **)

Math.pow(7, 2); // 49

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

DOM (объектная модель документов)

• Несовместимость браузеров Firefox, Internet Explorer, Opera, Google Chrome, Safari, Konqueror и т.д. делает работу с DOM чрезвычайно трудным делом.
• Если имеется обработчик событий, вызывающий alert(), то он всегда прекращает событие, независимо от того, желаете вы этого или нет.

// Данный обработчик событий даёт возможность событию распространяться
function doNothingWithEvent(event) {
   return true;
}

// Данный обработчик событий прекращает распространение
function doNothingWithEvent(event) {
   alert('screwing everything up');
   return true;
}

Почему Lua отстой


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

do
  local realVar = "foo"
  real_var = "bar" -- Oops
end
print(realVar, real_var) -- nil, "bar"

• Разыменование на несуществующем ключе возвращает ноль вместо ошибки. Это вместе с вышеупомянутым пунктом делает орфографические ошибки в Lua опасными и, в основном, скрытыми.

• Если vararg (аргумент переменной длины) находится в середине списка аргументов, то только первый аргумент будет учтён.

local function fn() return "bar", "baz" end
print("foo", fn()) -- foo bar baz
print("foo", fn(), "qux") -- foo bar qux

• Одновременно можно держать только один vararg (аргумент переменной длины) (в ...).

• Невозможно сохранить varargs (аргументы переменной длины) для дальнейшего.

• Невозможно выполнять перебор varargs (аргументов переменной длины).

• Невозможно видоизменять varargs (аргументы переменной длины) непосредственно.

• Можно собрать varargs в таблицах, чтобы сделать все эти действия, но тогда необходимо будет позаботиться об исключении нулевых значений, которые имеют силу в varargs, но являются сигналом конца таблиц, как, например, \0 в C-строках.

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

• Выражения break, do while (while (something) do, repeat something until something) и goto существуют, но нет continue. Странно.

• Операторы отличаются от выражений, а выражения не могут существовать вне операторов:

>2+2
  stdin:1: unexpected symbol near '2'
>return 2+2
  4

• Строковая библиотека Lua по умолчанию обеспечивает только подмножество регулярных выражений, которое само несовместимо с обычными регулярными выражениями PCRE.

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

• Нет способа наложить ограничения на аргументы функции. «Безопасные» функции Lua представляют собой мешанину из кода проверки типа.

• Отсутствует модель объекта. Само по себе это не имеет большого значения, но приводит к некоторым противоречиям — строковый тип может быть обработан как объект, включая метатаблицу и строковые значения, вызванные этим методом. Это не действует для любого другого типа.

>("string"):upper()
  STRING (СТРОКА)
>({1,2,3}):concat()
  stdin:1: attempt to call method 'concat' (a nil value)
>(3.14):floor()
  stdin:1: attempt to index a number value

Почему PHP отстой


'0', 0 и 0.0 являются неправильными, но '0.0' — правильным.

• Это и множество других проявлений плохой конструкции вызывают у меня грусть.

• Нет какой-то одной непротиворечивой идеи о том, что представляет собой выражение. Имеются, как минимум, три: нормальное плюс следующие исключения:
• здесь doc-синтаксис "<<<END" не может быть использован в инициации значений по умолчанию атрибутов метода на PHP < 5.3.

• Документация не имеет версий. Имеется единственная версия документации, предлагаемая для использования с php4.x, php5, php5.1…

• Отсутствует общая концепция идентификатора. Некоторые идентификаторы (такие как имена переменных) чувствительны к регистру, другие — нет (как, например, вызовы функций):

$x = Array();
$y = array();
$x == $y; # is true
$x = 1;
$X = 1;
$x == $X; # is true

• Если неправильно ввести имя встроенной константы, то выдаётся предупреждение, и оно интерпретируется как строка «nonexistent_constant_name». Исполнение скрипта не останавливается.

• Если в вызов функции, задаваемый пользователь, введено слишком много аргументов, то ошибка не выдаётся; лишние аргументы будут проигнорированы.

• Это целенаправленное поведение для функций, которые могут принимать переменное количество аргументов (см. func_get_args()).

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

Array() является хэш-массивом в одном типе данных.

• «Хэш-массив» сам по себе — нормально. Однако упорядоченный хэш-массив — это просто беда. Рассмотрим:

$arr[2] = 2;
$arr[1] = 1;
$arr[0] = 0;
foreach ($arr as $elem) { echo "$elem "; } // печатает "2 1 0"!!

• Нет использования динамических областей действия идентификаторов.

• Имеется автовосстановление идентификатора с неэквивалентом "use strict".

• В дополнение к реализации POSIX STRFTIME(3) собственный язык форматирования данных.

• Неважный строковый интерполирующий преобразователь:

error_log("Frobulating $net->ipv4->ip");
Frobulating  Object id #5->ip

$foo = $net->ipv4;
error_log("Frobulating $foo->ip");
Frobulating 192.168.1.1

Однако здесь это будет работать как ожидалось:

error_log("Frobulating {$net->ipv4->ip}");

• Имеются два способа начать комментарий в конце строки: // и #.

• Код всегда должен находиться между тегами <?php и ?>, даже если это не HTML порождающий код, обсуждавшийся на соответствующей странице.

• Два имени для одного и того же типа с плавающей точкой: float и double.

• Имеются псевдотипы для определения параметров, которые принимают различные типы, но нет способа задать тип для специфического параметра, отличного от объектов, массивов или вызовов, начиная с PHP 5.4 (исправлено в PHP 7).

• Переполнение при целочисленной операции автоматически преобразует тип в плавающий (float).

• Имеются тысячи функций. При работе с массивами, строками, базами данных и т.п. приходится иметь дело с десятками функций, такими как array_diff, array_reverse и т.д. Операторы являются несовместимыми; например, массивы можно объединять лишь при помощи + (- не работает). Методы? Нет способа: $a.diff($b), $a.reverse() не существует.

• PHP является языком на базе C и Perl, который не является, по своему существу, объектно-ориентированным. И если вы знаете внутренние переменные для объектов, то есть основания почувствовать себя счастливым.

• Имена функций неоднородные: оба имени — array_reverse и shuffle — относятся к работе с массивами.

• Некоторые функции являются функциями «needle, haystack», тогда как другие — «haystack, needle».

• Доступ к символам строки может быть как через обычные скобки, так и фигурные.

• Изменяемые переменные создают неоднозначности с массивами: $$a[1] должно быть представлено как ${$a[1]} или ${$a}[1], если требуется использовать $a[1] или $aa в качестве переменной для ссылки.

• Изменяемые переменные, в общем случае, являются великим злом. Если ответом являются изменяемые переменные, то, определённо, задан неправильный вопрос.

• Константы могут быть заданы только для скалярных значений: логические (булевские) значения, целые числа, ресурсные единицы, числа с плавающей точкой и строки (они могут держать массивы согласно PHP 7).

• Константы не используют префикс $, как переменные, — что имеет смысл, так как они — константы, а не переменные.

! имеет больший приоритет, чем =, но не в этом — if (!$a = foo()) — «специальном» случае!

• В 32- и 64-битных системах операторы сдвига (<< >> <<= >>=) дают разные результаты для более чем 32 сдвигов.

• Встроенные классы (их экземпляры) можно сравнивать, но только если они не являются определяемыми пользователем.

• Массивы могут оказаться «несравнимыми».

•Операторы and и or совершают то же действие, что && и ||, но только с разным приоритетом.

• Как фигурные скобки, так и : с последующим endif;, endwhile;, endfor; или endforeach разделяют блоки для соответствующих операторов.

• Для преобразования в целые числа имеются (int) и (integer), в логические значения — (bool) и (boolean), в числа с плавающей точкой — (float), (double) и (real).

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

• Это вызывает фатальную ошибку на PHP5 — если вы не используете объект, реализующий функциональные возможности массива, давая вам то, что работает как объект И массив (и создаёт проблемы).

• Включаемый файл наследует область видимости переменной строки, в которой происходит включение, но функции и классы, определённые во включаемом файле, имеют глобальную область видимости.

• Определения класса и функции всегда имеют глобальную область видимости.

• Если некоторый включаемый файл должен вернуть какое-то значение, но этот файл не может быть включён, то оператор include возвращает FALSE и только выдаёт предупреждение.

• Если требуется какой-то файл, то необходимо использовать require().

• Функции и классы, определённые внутри других функций или классов, имеют глобальную область видимости: Они могут быть вызваны за пределами исходной области видимости.

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

function makeyogurt($type = "acidophilus", $flavour)
{
  return "Making a bowl of $type $flavour.\n";
}

echo makeyogurt("raspberry"); // печатает "Изготовление вазы для малины". Будет только выдано предупреждение.

• Методы (PHP 4) могут быть вызваны как методы экземпляра класса (с заданной специальной переменной $this) и как методы класса (с self).

•Если класс объекта, который должен быть рассериализирован, не определён при вызове unserialize(), то взамен будет создан экземпляр __PHP_Incomplete_Class, теряющий любую ссылку на исходный класс.

• Оператор цикла перебора массивов foreach, применённый к объекту, выполняет итерацию через его переменные по умолчанию.

• Статические ссылки на текущий класс, такие как self:: или __CLASS__, работают при использовании класса, в котором определена данная функция (может быть выполнено в PHP >= 5.3 с static::):

class A {
  public static function who() {
    echo __CLASS__;
  }
  public static function test() {
    self::who();
  }
}
class B extends A {
  public static function who() {
    echo __CLASS__;
  }
}
B::test(); // печатает A, не B!

• Вложенные классы (класс, определённый внутри некоторого класса) не поддерживаются.

class a {
   function nextFoo() {
      class b {} // не поддерживается
   }
}


• Глобальные объекты (параметры, переменные) не всегда «глобальные»:

$var1 = "Example variable";
$var2 = "";
function global_references($use_globals)
{
  global $var1, $var2;
  if (!$use_globals) {
    $var2 =& $var1; // видимость обеспечена только внутри данной функции
  } else {
    $GLOBALS["var2"] =& $var1; // видимость обеспечена также в глобальном контексте
  }
}
global_references(false);
echo "var2 is set to '$var2'\n"; // var2 установлена на ''
global_references(true);
echo "var2 is set to '$var2'\n"; // var2 установлена на ''Модельная переменная"


• Отсутствует концепция модуля/пакета: только вложенные файлы, «как в С».
• Значительная часть функциональных возможностей PHP обеспечена через скомпилированные модули, написанные в С.

• Нет способа сохранить 64-битные целые числа в собственном типе данных на 32-битовой машине, что ведёт к несообразностям (intval('9999999999') возвращает 9999999999 на 64-битовой машине, но — 2147483647 на 32-битовой).

• Класс, представляющий файл, называется: SplFileObject.

SplFileObject расширяет файловый метаобъект SplFileInfo и одновременно является его свойством. Невозможно выбрать между наследованием и композицией? БУДЕМ ИСПОЛЬЗОВАТЬ ТО И ДРУГОЕ!?

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

• От этого у меня просто едет голова:

in_array("foobar", array(0)) === true

• Причина в том, что при выполнении нестрогих сравнений строка «foobar» принудительно вставляется в целое число, подлежащее сравнению с 0. Чтобы получить ожидаемый результат, необходимо установить для in_array флажок strict, который, очевидно, использует === вместо == для обеспечения подлинного тождества, а не просто какого-то типа равенства.

php.ini может изменить всё поведение ваших скриптов, сделав их непереносимыми между различными машинами с различным файлом настройки (установочным файлом). Таким образом, он является единственным скриптовым языком с файлом настройки.

null, "", 0 и 0.0 — все они равны.

• Числа, начинающиеся с 0, являются восьмеричными, поэтому 08, 09, 012345678 и подобные порождают ошибку. Вместо этого, любые цифры после первых 8 или 9 просто игнорируются: 08 == 0, 08 != 8, 0777 == 07778123456 (исправлено в PHP 7).

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

Исправлено в поддерживаемых в настоящее время версиях

До PHP 5.5 действовали приведённые далее положения. Более старые версии не поддерживаются (как 04/19/2016):

• Фатальные ошибки не содержат обратную трассировку или трассировку стека (исправлено в PHP 5).

• Применение [] или {} к переменной типа не массив или строка даёт обратно NULL (исправлено в PHP 5).

• Имеются конструкторы, но нет деструкторов (исправлено в PHP 5).

• Имеются «методы класса», но нет (статических) переменных класса (исправлено в PHP 5).

•Ссылки в конструкторах не работают (исправлено в PHP 5):

class Foo {
  function Foo($name) {
    // создаёт ссылку внутри глобального массива $globalref
    global $globalref;
    $globalref[] = &$this;
    $this->setName($name);
  }
  function echoName() {
    echo "\n", $this->name;
  }
  function setName($name) {
    $this->name = $name;
  }
}
$bar1 = new Foo('set in constructor');
$bar1->setName('set from outside');
$bar1->echoName(); // печатает "установить извне"
$globalref[0]->echoName(); // печатает "установить в конструкторе"

// Необходимо снова сослаться на возвращённое значение, чтобы получить назад ссылку на тот же объект:

$bar2 =& new Foo('set in constructor');
$bar2->setName('set from outside');
$bar2->echoName();         // печатает "установить извне"
$globalref[1]->echoName(); // печатает "установить извне"

• Метод, определённый в базе, может «волшебным образом» стать конструктором для другого класса, если его имя соответствует классу первого (исправлено в PHP 5):

class A
{
  function A()
  {
    echo "Constructor of A\n";
  }
  function B()
  {
    echo "Regular function for class A, but constructor for B";
  }
}
class B extends A
{
}
$b = new B; // вызов B() как конструктора

• Исключения в функции __autoload не могут быть перехвачены, что приводит к фатальной ошибке (исправлено в PHP 5.3).

• Нет поддержки закрытия; create_function не учитывается, так как она принимает строку в качестве аргумента и не ссылается на область, в которой она была создана (исправлено в PHP 5.3).

• Если функция возвращает массив, то вы просто не можете писать (исправлено в PHP 5.4).

$first_element = function_returns_array()[0]; // Синтаксическая ОШИБКА!!
$first_element = ( function_returns_array() )[0]; // Это ни то, ни другое!!
// Взамен надо написать:
$a = function_returns_array();
$first_element = $a[0];

Почему Perl 5 отстой


Perl хуже, чем Python, потому что люди хотели, чтобы он был хуже. Ларри Вол, 14 окт. 1998.

• «use strict» («строгий режим») — на самом деле, должно быть «use unstrict» («нестрогий режим») или «use slop» («нечёткий режим») (как в бильярде/пуле), что изменяет ситуацию и что само по себе никогда не должно использоваться. В любой ситуации. Кем бы то ни было. «Строгий» режим должен быть по умолчанию.

use strict;
use warnings;


• Вариантность символов чрезвычайно раздражает.

my @list = ("a", "b", "c");
print $list[0];

• Нет списков параметров (если вы не используете Perl6::Subs).

sub foo {
  my ($a, $b) = @_;
}

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

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

• Очень тяжело задать тип скаляр, например, нет лёгкого способа определить, является ли строкой некоторая переменная.

• Попытайтесь разъяснить некоторую ссылку С-программисту. Он скажет: «О, это указатель!», — хотя это будет не так. Ну не совсем. Это похоже на указатель, или я просто так слышу. Будучи Perl-программистом я знаю, что это не совсем указатель, но я не знаю точно, что такое указатель (в то время, как я знаю, что такое ссылка, но я не могу объяснить это — это не смог бы и Ларри Уолл: он назвал это — «штучка»).

• Синтаксис регулярных выражений ужасен.

• Редко есть хорошее место, чтобы поместить точку с запятой после here-doc.

my $doc = <<"HERE";
  But why would you?
HERE
print $doc;

• Обычно требуется десять лет, чтобы понять, как вызывать функции или методы внутри строк, заключённых в двойные кавычки, как любая другая переменная. Хотя, если вы читаете это, вы добъётесь успеха: «Вы сделаете это @{[sub{'like'}]}. Легко».

• Так же, как Ruby, он имеет избыточное ключевое слово «unless» («пока не»). Можно выполнить операцию «if not», по крайней мере, тремя способами, что может привести к путанице и ухудшенной читаемости кода:

1. if (! выражение)
2. if (нет выражения)
3. unless (выражение)

• Я не могу использовать if($a==$b) $c=$d ;; вместо этого я должен употребить:

1. $c=$d if($a==$b); или
2. if($a==$b) { $c=$d; }

• Как обстоит дело со всеми этими $,@,%,& перед переменными? Требуются изрядные усилия, чтобы печатать все эти штучки каждый раз … С и большинство других языков позволяют задать тип один раз, а затем можно использовать его, не задумываясь, что это такое. В любом случае Perl позволяет изменять ход выполнения в зависимости от того, что вы желаете делать, поэтому ещё более глупо использовать, скажем, @ и $ перед одной и той же переменной.

• Вы не поймёте свою программу, когда снова выйдете на неё через 6 месяцев.

Почему Python отстой


• Проблема отступов — обычно её называют «проблемой пробелов»: относительная величина отступа у оператора определяет, на какой уровень цикла / условия / и т.д. он действует. Ссылаясь на приведённый ниже фрагмент псевдо-C, можно сказать, что цель была, по-видимому, в том, чтобы обойти своего рода явную глупость, которая не должна появляться на первом месте.

if(something)
  if(something_else)
      do_this();
else
  do_that();

Очевидно в С, что оператор «else», в действительности, относится ко второму оператору if(), несмотря на вводящий в заблуждение отступ; это не так в Python, где вместо изучения работы логического потока, вы просто вводите отступ, чтобы показать связь.

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

• Однако даже при этом бывают случаи, когда a.b() необязательно вызовет «b» как метод для «a»; другими словами, бывают случаи, когда a.b() не отправит «a» как «свой» аргумент; например:

class NoExplicit:
   def __init__(self):
      self.selfless = lambda: "nocomplain"

   def withself(): return "croak" #will throw if calld on an _instance_ of NoExplicit

a = NoExplicit ()

print(a.selfless()) #won't complain
print(a.withself()) #will croak

Это значит, что даже если выражение в форме a.b() выглядит как вызов метода, это не всегда так; возможно, это противоречит линии «всё должно быть чётко и предсказуемо, насколько это возможно», которую Python упорно пытается держать.

• Многие библиотеки возвращают кортежи из вызовов функций, и приходится перерывать гору документации, чтобы выяснить, что же означают эти поля кортежей; если бы они взамен вернули dicts как в JavaScript, то имена полей часто были бы очевидны без документации.

• Синтаксис кортежей, х, довольно «деликатный». При добавлении запятой в какое-то выражение оно превращается в кортеж. Это ведёт к ошибкам, которые трудно обнаружить:

foo = 1.0 + 2 # Foo is now 3.0
foo = 1,0 + 2 # Foo is now a tuple: (1,2)
foo = 3 # Foo is now 3
foo = 3, # Foo is now a tuple: (3,)

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

(1) == 1                      # (1) is just 1
[1] != 1                      # [1] is a list
1, == (1,)                    # 1, is a tuple
(1) + (2) != (1,2)            # (1) + (2) is 1 + 2 = 3
[1] + [2] == [1,2]            # [1] + [2] is a two-element list
isinstance((), tuple) == True # () is the empty tuple

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

Прерывание или продолжение по метке отсутствуют.

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

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

• Отсутствует оператор типа "do ... until <condition>", что заставляет использовать модели вроде "while not <condition>:"

• Синтаксис для условного выражения в Python является неудобным (x if cond else y) Сравните с С-подобными языками: (cond? x: y).

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

• Нет констант.

• Нет интерфейсов, хотя абстрактные базовые классы являются шагом в этом направлении.

• Имеется, по крайней мере, 5 различных типов (несовместимых) списков [1].

• Непоследовательность в использовании методов/функций — некоторые функциональные возможности требуют использования методов (например, list.index()), а другие — функций (например, len(list)).

• Нет синтаксиса для многострочных комментариев, идиоматический Python злоупотребляет многострочным синтаксисом строк вместо

"""

• Сосуществование Python2.x и Python3.x на системе Linux создаёт большие проблемы.

• Имена функций с двойным символом подчёркивания спереди и сзади представляют собой неприглядное явление:

__main__

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

if __name__ == "__main__":

• Типы символов строки перед строкой — ужасное зрелище:

f.write(u'blah blah blah\n')

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

• Генераторы определяются использованием ключевого слова «yield» в теле функции. Если Python встречает одинокое слов «yield» в вашей функции, то эта функция превращается в генератор, и любой оператор, который возвращает что-либо, становится синтаксической ошибкой.

• Python 3.5 вводит ключевые слова «async» и «await» для задания сопрограмм. Python претендовал на то, чтобы получить сопрограммы через генераторы с ключевыми словами «yield» и «yield from», но вместо исправления ситуации с генераторами была добавлена другая проблема. Теперь имеются асинхронные функции, а также нормальные функции и генераторы, а правила, определяющие ключевые слова, используемые внутри них, стали ещё более сложными [2]. В отличие от генераторов, где всё, содержащее ключевое слово «yield», становится генератором, всё, что использует сопрограммы, должно начинаться с префикса «async», в т.ч. «def», «with» и «for».

• Вызов super() является особым случаем компилятора, который работает по-другому, если он переименован [3].

• Стандартная библиотека использует несовместимые схемы назначения имён, например: os.path.expanduser и os.path.supports_unicode_filenames (прежняя не различается слова с нижним подчёркиванием, тогда как последняя делает это).

Исправлено в Python 3

!= может быть записано также как <> (см. php).

• Неполная встроенная поддержка комплексных чисел: как (-1)**(0.5), так и pow(-1, 0.5) выдают ошибку вместо возврата 0+1j.

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

Почему Ruby отстой


String#downcase? Кто называет это «downcase»? Правильное название — «lower case,» и метод должен называться «lowercase» или «lower». А String#upcase должен называться «uppercase» или «upper». Это, на самом деле, не делает Ruby плохим — дело только личного вкуса. Всё же лучше, чем история с tw… PHP.

• Поддержка Unicode должна была быть встроена, начиная с версии 1.0, но не была добавлена и после множества выражений недовольства в 1.9/2.0 в 2007 году.

• Нет поддержки ретроспективной/опережающей проверки в регулярных выражениях в версии 1.8.

• Регулярные выражения всегда находятся в многострочном режиме.

• (Начиная с Ruby 2.0, не действует!) Нет реальной поддержки произвольных именованных аргументов (пары ключ=значение в определениях функций являются позиционными аргументами со значениями по умолчанию).

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

• Нет продуманных и тщательно планируемых изменений, проводимых так, чтобы они не могли нарушить совместимость; даже небольшое редактирование может привести к её нарушению: см. «Проблемы совместимости» и «файловые утилиты». Это ведёт к нескольким рекомендованным стабильным версиям: как 1.8.7, так и 1.9.1 для Windows. Какую из них использовать?

• Экспериментальные и (как известно) содержащие множество ошибок свойства добавлены в рабочие и «стабильные» версии: см. «прохождение блока в Proc».

• Документация не проверена: она содержит недоступные ссылки, как, например, «Что должен знать любой начинающий».

• Имеется несколько небольших «глюков». nil.to_i превращает «nil» в 0, но 0 не расценивается как «nil». nil.to_i.nil? #=> false.

String#to_i просто игнорирует замыкающие символы, а это означает: "x".to_i == 0.

• Ruby позволяет пользователям модифицировать встроенные классы, что может быть полезным, однако ограниченное пространство имён означает, что расширения могут конфликтовать. Опытные программисты знают, что можно добавить функциональные возможности через модули, а не заниматься партизанским латанием встроенных классов, но всё же склонны к злоупотреблению. Эта проблема была решена в Ruby 2.0.

• Методы с назначенными псевдонимами в стандартной библиотеке делают чтение кода, написанного другими, более трудным, если читающий ещё не очень хорошо знаком с основным материалом. Например, Array#size/Array#length, Array#[]/Array#slice.

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

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

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

• Суффиксные условия после целых блоков кода, например, begin ... rescue ... end if expr. Гарантированно будет потеряно if expr, если блок кода содержит несколько строк.

• Ключевое слово unless (действует как if not) делает, как правило, код труднее для понимания для некоторых людей.

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

• «Утрачивающие значение» функции. Значением последнего выражения в какой-то функции, как предполагается, является возвращаемое значение. Необходимо явно прописать nil, если требуется сделать вашу функцию «void» («пустая») (процедура). Это похоже на ситуацию, когда вы, действительно, заботитесь о том возвращаемом значении.

• pre-1.9: нет способа получить stdout, stderr и код завершения (все сразу) какого-то подпроцесса.

`` синтаксис со строковой интерполяцией для текущих подпроцессов. Это делает лёгкой атаку с внедрением в оболочку.

• Регулярные выражения волшебным образом назначают переменные: $1, $2,…

• Стандартные контейнеры (Array, Hash) имеют очень большие интерфейсы, что затрудняет их эмулирование. Поэтому эмулировать не следует — надо взамен наследовать. class ThingLikeArray < Array; end.

• Разрешены как символы, так и строки, и они часто используются как ключи в хэшах, но "foo" != :foo, что ведёт к нововведениям, как, например, HashWithIndifferentAccess.

• Ошибки анализатора могли бы быть выражены более ясно. «синтаксическая ошибка, неожиданный kEND, возможный $end» фактически означает «синтаксическая ошибка, неожиданный 'конец' ключевого слова, возможный конец ввода».

• Символы неодинаковые, если их закодированные байты не равны, даже если они представляют одни и те же строки.

Почему Flex/ActionScript отстой


• Класс String определён как завершённый, поэтому, если требуется добавить какую-то функцию к незавершённому классу String, как, например, startsWith или hashCode, то необходимо выполнить pass thrus для всех методов String.

• Переменные метода действительны для всего метода, а не только для текущего блока кода. (Следует отметить, что это является недостатком также и в JavaScript).

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

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

Почему скриптовые языки отстой


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

Почему С отстой


• Ручное управление памятью может быть утомительным.

• Обращение со строками является частью ручного управления памятью. См. выше.

• Поддержка параллельного программирования довольно слабая.

• Ужасные названия стандартных функций: isalnum, fprintf, fscanf и т.д.

• Препроцессор.

• Не было сильных ощущений сегодня? Выполните аварийное завершение программы.

• Нехватка достоверной информации при аварийном завершении программы… «Стандартный» набор инструментальных средств GNU не информирует вас о случаях аварийного завершения программы во время стандартного прогона.

• Если программа унаследованная, то через пару дней у вас будет совсем не геройский вид.

• Стандартная библиотека охватывает только основные манипуляции со строками, ввод-вывод файлов, а также распределение памяти. И qsort() по какой-то причине. Вы не получаете ничего такого, что является по-настоящему переносимым.

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

• Также отсутствует стандарт для сетевого программирования. Большинство платформ использует интерфейс прикладного программирования (API) сокетов Berkeley, но он сильно зависит от поставщика. В частности, API от Microsoft имеет много необычных особенностей. Хотя можно пожелать удачи в поиске переносимого O (1) API выбора асинхронного сокета; нет ни одного (select() и poll() имеют характеристику O(n)).

• Автоматическая инициализация переменных в нуль не происходит, хотя они являются статическими, поскольку «это более эффективно».

• Есть желание использовать С99 в переносимом виде? Конечно; но если вам потребуется использовать MSVC, более старый чем 2015 (вполне вероятно у некоторых разработчиков), то ничего не получится.

• Есть желание использовать С11 в переносимом виде? Конечно, но в значительной степени никто, кроме GCC и случайных троллей, не поддерживает этого. Извините, пользователи MSVC.

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

• malloc() берёт один параметр размера (размер в байтах), но calloc() — два (количество элементов и размер элемента в байтах).

• «Undefined behaviour» («Неопределённое поведение») — основной лейтмотив в С. Под «неопредёлённым» разработчики здесь понимают «Добро пожаловать делать всё, что желает поставщик, без необходимости учитывать, чего желаете вы». Самое плохое в этом то, что невозможно надёжно найти все случаи «неопределённого поведения» в какой-нибудь большой программе.

int a = 0; f(a, ++a, ++a); не гарантировано, что будет f(0, 1, 2). Порядок вычисления не определён.

• Еще один сюрприз, который «впечатляет»: i[++a] = j[a];, поскольку не определено, какая сторона рассчитывается сначала.

• Знаковое переполнение является технически неопределённым поведением. Это происходит на большинстве платформ.

• Сдвиг на более чем N битов на типе intN_t является неопределённым поведением.

• Преобразование типа int * во float * с последующим разыменованием является неопределённым поведением. Необходимо использовать memcpy().

• Разыменование указателя NULL является неопределённым поведением. Здесь нет подвоха — можно получить действительную память из этого!

• Преобразование типов между указателями функции и указателями данных является технически неопределённым поведением; на некоторых платформах они имеют разные размеры (почему это должно заботить программиста в C, оставим как упражнение для читателя).

• Преобразование типа void (*)() в int (*)(int, const char *), действительно, должно быть неопределённым? Нет! Все указатели функции могут быть преобразованы друг в друга. Хотя фактически вызов функции после преобразования, если тип неправильный, является неопределённым поведением (как можно было бы с полным правом и ожидать этого).

• Преобразование чего-нибудь вроде, скажем, FILE * в double * и снова обратно не является неопределённым, поскольку в промежутке не производится разыменование. Все указатели данных являются эквивалентными.

• Отладка оптимизированного кода может иногда не иметь смысла из-за агрессивной оптимизации компилятора.

• Безопасность данных в многопоточных программах, в действительности, не гарантируется языком; то, что, как вы полагаете, является элементарным, не обязательно является таковым без использования (C11, но см. выше) элементов. Получить нарушения при чтении и записи чрезвычайно просто, и никакие инструменты никогда не предупредят вас, что это происходит.

• Указатели на объект, тип которого неизвестен, создают проблемы; нет никакого способа определить, что на самом деле стоит за ними без какой-то определённой пользователем схемы маркировки, которая по определению не может содержать все типы. Решением является, несомненно, — «будьте внимательными».

• Система типов в общем случае является бесполезной, поскольку можно преобразовать (почти) всё во всё.

Почему С++ отстой


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

• Он имеет обратную совместимость с С.

• Однако имеются небольшие различия, из-за которых некоторые C-программы не удаётся скомпилировать компилятором C++.

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

• C++ не обеспечивает единой парадигмы. Не применяются ни процедурные, ни объектно-ориентированные парадигмы, что приводит к ненужному усложнению. [Некоторые рассматривают это как преимущество.]

• Процессы введения в действие и изучения очень трудные: описание превышает 1 000 страниц.

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

• Стандарт не имеет реализации для обработки исключений и декорирования имени. Это делает объектный код кросс-компилятора несовместимым.

• Широко используемая операционная система не поддерживает C++ ABI для системных вызовов.

• Что такое 's' — функция или переменная?

std::string s();

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

• Инициализированная по значению переменная 's' должна бы быть следующей:

std::string s = s(); /* or */ std::string s{};

• Есть try, но не finally.

• Рассмотрена «особенность», потому что RAII, как предполагается, является главным средством распоряжения ресурсами в C++. Исключением являются многие библиотеки, которые не используют RAII.

• Чрезвычайно плохая поддержка Unicode.

•Операторы могут быть перегружены, только если есть, по крайней мере, один параметр класса.

• Это также делает невозможными строки массива сцепленных символов, что иногда заставляет программистов использовать ужасные функции C, такие как strcat.


catch (...) не позволяет узнать тип исключения.

throw в сигнатурах функций совершенно бесполезен.

• Система исключений не связана с платформой: разыменование указателя NULL не породит исключение C++. [Некоторые люди рассматривают это как преимущество.]

• Спецификатор mutable трудно использовать целесообразно, и, поскольку это ухудшает спецификатор константного объекта const и, тем самым, потоковую безопасность, то могут легко возникнуть небольшие ошибки взаимосовместимости.

• Замкнутые выражения должны быть представлены в явном виде в лямбда-выражениях (никогда не слышал о чём-либо подобном ни в одном функциональном языке).

• Можно использовать [=] и поместить всё, но это увеличивает словесное наполнение.

• Природа C++ заставила разработчиков писать код, зависящий от компилятора, что порождает несовместимость между различными компиляторами и даже разными версиями одного и того же компилятора.

• Вызов std::string::c_str() требуется для преобразования std::string в char*. Из самого мощного языка мы всегда имели бы полностью принимаемый перегруженный operator const char* () const.

• Разработчикам, возможно, придётся побеспокоиться о вопросах оптимизации, таких как, например, объявлять функцию inline (встраиваемой) или нет; но после принятия такого решения оно является только предложением: компилятор может решить, что предложение неправильное, и не принять его. В чём смысл? Должны ли разработчики беспокоиться о вопросах оптимизации?

• Чтобы исправить эту ситуацию, многие компиляторы применяют __forceinline или аналогичное.

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

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

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

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

Почему .NET отстой


• SortedList использует пары «ключ — значение». Нет стандартной .NET-совокупности для списка, которая сохраняла бы элементы в отсортированном порядке.

• Изменение какой-то совокупности (коллекции) делает недействительными все операции итерации в каждом потоке, даже если текущий элемент не влияет в данный момент. Это значит, что каждый оператор цикла перебора массивов «foreach» может сгенерировать и сгенерирует исключение, когда вы меньше всего этого ожидаете. Так будет, если вы не используете блокировки, которые, однако, в свою очередь, могут привести к взаимоблокировкам. Может быть преодолено с помощью формальных параллельных или неизменяемых совокупностей (коллекций) или при итерации путём использования «for» вместо «foreach».

• MSDN-документация (гипотетического) GetFrobnicationInterval должна была бы разъяснить, что тот возвращает значение интервала, на котором объект бесцельно манипулирует (например, клавиатурой или мышью). Она должна была бы также указать, что этот метод выдаст InvalidOperationException, если указанный интервал не может быть найден. Вы также найдёте два «комментария сообщества», один из которых будет наполнен бессмысленным набором символов, а в другом будет запрос на ломаном английском о том, как происходит бесцельное манипулирование клавиатурой при комнатной температуре.

Почему С# отстой


• Стандарты ECMA и ISO для C# были обновлены, начиная с C# 2.0; с тех пор существует спецификация только от Microsoft.

• i++.ToString работает, но ++i.ToString — нет. (Необходимо использовать круглые скобки.)

• Параметры не поддерживаются для большинства свойств, только индексаторы, даже если это возможно в Visual Basic .NET.

• Соглашения являются трудными для анализа кода и отличаются от соглашений других распространённых языков (схожего стиля).

• Почти всё выполнено в варианте Pascal (SomeClass, SomeConstantVariable, SomeProperty).

• Невозможно различить «extends» и «implements» без использования венгерской нотации (IInterface).

• Продвигает устаревшее (типы вариантов и LINQ, что существенно добавляет беспорядка).

•«out»-параметры (с синтаксической обёрткой).

• Вы не можете переопределить виртуальное или абстрактное свойство, которое имеет метод чтения ИЛИ метод записи, на другое, которое имеет метод чтения И метод записи, что делает жизнь довольно трудной в части наследования свойств. (Хотя это не применимо к интерфейсам, см. http://stackoverflow.com/questions/82437/why-is-it-impossible-to-override-a-getter-only-property-and-add-a-setter.)

• Невозможно выполнить какие-либо операции (даже простейшие арифметические) с объектами внутри обобщённого метода (например, T plus<T>(T t1, T t2) { return t1+t2; }.

• Невозможно назначить новое значение внутри цикла «foreach» (например, foreach(int i in vec) { i = i+1; }.

Выполнение IDisposable правильно представляется очень сложным. (В частности, примитивная реализация не будет проведена, если финализатор вызван сборщиком мусора.)

Почему VB.NET отстой


• Опция Strict Off: она позволяет выполнить неявное преобразование, когда компилятор решает, что это уместно, например, lang="text">Dim a As Integer = TextBox1.Text ' (происходит преобразование строки в целое число при использовании Microsoft.VisualBasic.CompilerServices.Conversions.ToInteger).

• Опция Explicit Off: автоматически объявляет локальные переменные типа Object везде, где используется необъявленная переменная. Обычно используется с опцией Strict Off.

On Error Goto и On Error Resume Next: это — процедурные способы скрыть ошибки или отреагировать на них. Чтобы действительно перехватить исключение, необходимо использовать Try-Catch-Block (блок попытка-перехват).

• Множество функций для совместимости вниз, таких как UBound(), MkDir(), Mid(),… Они могут быть скрыты при удалении импорта по умолчанию для пространства имён Microsoft.VisualBasic в настройках проекта.

• My-Namespace (Моё пространство имён) (исключение составляют My.Resources и My.Settings, которые могут быть очень полезными). Всё в этом пространстве имён является менее гибкой версией имеющегося элемента, например, My.Computer.FileSystem.WriteAllText vs System.IO.File.WriteAllText.

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

• Методы расширения могут быть заданы только в модулях.

• Методы расширения не могут быть применены к любым образом типизированному объекту, даже когда динамическое связывание (опция Strict Off) отключено. См. данную статью о StackOverflow.

• The Microsoft.VisualBasic.HideModuleNameAttribute. Это является необязательным из-за природы модулей.

• Экземпляры по умолчанию для форм. Действительным является написание

Form2.InstanceMethod()

Вместо
Dim Form2Instance As New Form2
Form2Instance.InstanceMethod()

поскольку в результате компиляции получается следующее:
MyProject.Forms.Form2.InstanceMethod()

• Объединение строк может быть проведено при помощи + и & вместо просто &. + путает новых программистов, особенно если у них опция Strict установлена на Off.

Почему VBA отстой


• Индексы массива начинаются с нуля, но индексы коллекции — с единицы.

• У классов нет конструкторов, которые могут принять аргументы.

• Невозможно перегрузить методы.

• Нет наследования.

• Поддерживает GoTo.

• `OnError Resume Next` — Yeah… Происходит именно то, что вы читаете. Обнаружена ошибка? Нет проблемы! Просто продолжайте упираться и продвигаться вперёд в следующей строке.

• Свойства по умолчанию.

Приведённые ниже две строки означают не одно и то же.
Dim myRange As Variant
myRange = Range("A1")
Set myRange = Range("A1")

Одна задаёт для `myRange` значение «A1», тогда как другая делает его текущим объектом Range.

Почему Objective-C отстой


• Нет какого-либо реального использования за пределами того программирования для OS X и iOS, которое не может быть сделано с помощью другого языка на базе C; это значит, что ваш набор навыков не удастся применить за рамками вполне ограниченного рынка.

• Использование Objective-C в переносимом варианте является сочетанием несочетаемого — оксюмороном; GNUStep является менеджером окон для Fringe на Linux, и нет ничего на самом деле там для Windows, вообще… Единственное, что работает, так это компилятор.

• Нет перегрузки оператора.

• Для перегрузки метода существует «обходной путь».

• Попытки втиснуть язык Smalltalk с динамической типизацией в язык C со статической типизацией.

• Нет объектов со стековой архитектурой.

• Синтаксис очень странный по сравнению с другими языками (я должен вставлять @ перед кавычкой, создавая строку?! Вызов методов происходит аналогично за исключением случаев, когда имеется единственный аргумент?!? [methodName args];)

http://fuckingblocksyntax.com

• Техническое описание, вообще, отсутствует. Никто (за исключением, может быть, некоторых разработчиков LLVM и Apple), в действительности, не знает, что происходит «под капотом».

• Может случайно рухнуть при возвращении SEL из метода [4].

• Ужасная система типов. В качестве типов здесь выступает то, что можно назвать, скорее, рекомендациями.

• Objective-C++ и сплошная жуть, связанная с ним.

• Классы Objective-C и C++ не могут наследовать друг друга.

• Пространство имён C++ не может, вообще, взаимодействовать с кодом Objective-C.

• Передача параметров по значению C++ не может быть применена к объектам Objective-C в функциях C++; необходимо использовать указатели.

• Анонимные функции C++ и блоки Objective-C различаются и не являются взаимозаменяемыми.

• Классы Objective-C не могут иметь членов, которые являются классом C++, не имеют конструктора по умолчанию или имеют один или несколько виртуальных методов… за исключением введённых через указатели и назначенных через new.

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

• Objective-C не имеет стандартной библиотеки. Apple управляет фактически одной и всеми с префиксом NS*.

• Сосредоточенность Apple на Swift (который сам по себе является ещё одним специфическим для платформы языком) означает, что Objective-C оставлен существовать без внимания к нему.

Почему Java отстой


Синтаксис

• Чрезмерная многословность.

• Java имеет неиспользуемые ключевые слова, такие как goto и const.

• Нет перегрузки оператора… за исключением строк. Поэтому для псевдочисловых классов, таких как BigInteger, необходимо делать такие операции как a.add(b.multiply(c)), что выглядит довольно неуклюже.

• Нет делегатов; каждый раз, когда нужен указатель на функцию, приходится реализовывать заводскую разработку.

• Массивы не работают с обобщёнными типами: невозможно создать массив с типом переменной new T[42], бокс массива требуется, чтобы сделать это:

class GenSet<E> { Object[] a; E get(int i){return a[i];}}

• Нет свойств. Простые определения класса имеют длину в 7-10 раз больше, чем требуется.

• Нет буквенных констант для карт или массивов. Массив и карта являются интерфейсами коллекций.

• Нет ключевого слова var для предполагаемых локальных типов (как в C#). Учтите, что здесь дан пример плохой конструкции и что имена классов ни в коем случае не должны быть такими длинными. Пример:

// В Java
ClassWithReallyLongNameAndTypeParameter<NamingContextExtPackage> foo = new ClassWithReallyLongNameAndTypeParameter<>();
// В C# | Могло бы легко быть просто:
var foo = new ClassWithReallyLongNameAndTypeParameter<NamingContextExtPackage>();
// В Java | То же самое происходит для вызовов функции:
SomeTypeIHaveToLookUpFirstButIActuallyDontReallyCareAboutIt result = getTransactionResult();

• Вроде бы, невозможно написать в Java без IDE с автозаполнением, генерацией кода, управлением импортом и рефакторинга.

• Нет пар или троек. Возврат двух значений из функции или помещение пары в набор обеспечивает новый класс в новом файле. Параметризованный класс Pair ведёт к «кудрявым» типам повсюду.

Исправлено в Java 7 (2011)

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

Нет автоматической очистки ресурсов; взамен мы получаем пять строк «allocate; try {...} finally { cleanup; }».

Модель

• Нет функций и классов высшего порядка.

• Проверяемые исключения являются экспериментом, который провалился.

• Имеются типы int и объекты Integer, типы float и объекты Float. Поэтому можно иметь эффективные типы данных или объектно-ориентированные типы данных.

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

• До Java 8 использование только интерфейсов для реализации различных наследований не позволяло совместное использование общего кода для них.

Библиотека

• Функции в стандартной библиотеке не используют согласованное назначение имён, соглашения по сокращениям и заглавным буквам, что делает трудным запоминание названий элементов:

java.net имеет URLConnection и HttpURLConnection: почему не UrlConnection или HTTPURLConnection или HttpUrlConnection?

java.util имеет ZipOutputBuffer и GZIPOutputBuffer: почему не ZIPOutputBuffer или GnuZipOutputBuffer или GzipOutputBuffer или GZipOutputBuffer?

• Это, на самом деле, часть стандарта; необходимо писать всё прописными буквами, если имеется 3 или менее букв, или делать прописной только первую букву, если букв больше, т.е. RPGGame, а не RpgGame, и TLSConnection, но следует использовать Starttls.

• Конструкция за Cloneable и clone просто сломана.

• Массивы являются объектами, но неправильно используют .toString() (если попытаться распечатать массив, то получите просто тарабарщину из хэш-кода) или .equals() (массивы с одинаковым содержанием не считаются равными, что создаёт проблемы при попытке ввести массив в коллекции).

• До Java 8 полезные методы, такие как, например, сортировка, двоичный поиск и т.п., не были частью классов Collection (Коллекция), а были частью «вспомогательных классов», таких как Collections (Коллекции) и Arrays (Массивы).

• Почему Stack — класс, а Queue — интерфейс?

Stack принадлежит старой коллекции API, и его больше не следует использовать. Взамен используйте Deque (интерфейс) и ArrayDeque (реализация).

• Код загромождён преобразованиями типа. Массивы в списки, списки в массивы, java.util.Date в java.sql.Date и т.д.

• Программный интерфейс данных (Date API) считается устаревшим, но до сих пор повсеместно используется. Плана замены нет.

• До Java 8 не было функции объединения строк.

• Программный интерфейс Reflection API требует несколько строк кода для простейших операций.

• Регулярное выражение (a|[^d]) преобразует StackOverflowException в длинные строки.

• Отсутствуют беззнаковые числовые типы.

Обсуждение

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

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

• Некоторые интерфейсы, такие как Serializable и RandomAccess, используются почти так же как комментарии: они пустые, а если заполнены, то единственной их целью является отразить какую-то семантику.

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

• Массивы являются небезопасными по типу: Object[] foo = new String[1]; foo[0] = new Integer(42); компилируется прекрасно, но вызывает аварийное завершение.

• Управляющие символы Unicode могут иметь неожиданные результаты, поскольку они добавляются до того, как код подвергается синтаксическому анализу, и поэтому они могут повредить ваш код, например: (1) если комментарий конца строки содержит \u000A (возврат строки), то остаток комментария не будет на той же строке и не будет в комментарии вообще; (2) если строковый литерал содержит \u0022 (двойная кавычка), то строковый литерал будет завершён, а остаток строки войдёт в текущий код; (3) если в каком-то комментарии появляется \u и эта комбинация не является действительно управляющей (как, например, «c:\unix\home»), то будет выдана ошибка анализа, хотя имеем дело с комментарием.

• Вспомогательные функции должны быть перегружены для каждого фундаментального типа (например, max(float,float), max(double,double), max(long,long)).

Почему Backbase отстой


• О, эта тема может занять целую новую википедию сама по себе.

Почему XML отстой


• Атрибуты, как правило, ограничены неструктурированными данными.

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

• Многословие библиотеки стандартных текстов плохо согласуется с системами управления версиями, такими как Git. Записи одного типа запускаются и заканчиваются одинаково, даже если их содержание полностью различается. Это сбивает с толку программы автослияния (которые часто не понимают, как работают теги), заставляя принимать одну запись за другую, и нередко требует проведения слияния вручную, чтобы избежать искажения.

• Имеет место перепутывание метаданных и контента.

• Используется для обмена данными, когда это, на самом деле, просто формат кодирования данных.

• Нет способа по умолчанию для кодирования в двоичном представлении.

Почему отстой XSLT/XPath


• Нумерация начинается с 1. В отличие от *каждого отдельного другого* основного языка программирования, используемого в настоящее время. В отличие от XML DOM.

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

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

• Условные переходы в атрибутах test="" элементов <xsl:if> и <xsl:when>.

• Условия сортировки в <xsl:sort>.

• Если вашим контекстом является контент какого-то узлового набора, то функции key(), определённые на целом входе XML, не работают. И что ещё глупее — не выдаётся никакого сообщения об ошибке! Ваш select="key(...)" просто молча возвращает пустое множество. Должно было бы придти, по крайней мере, «key() не работает внутри узловых наборов» или, возможно, «нет такого ключа в контекстном узловом наборе».

• Атрибуты select="", value="" и match="" делают, по существу, то же самое. И их использование является произвольно исключительным; вы обязаны использовать правильный атрибут в правильном месте, а если вы что-то подставляете неправильно, то узел выпадает без какого-либо предупреждения. Эти три атрибута должны иметь одно и то же имя.

• Если вы импортируете какую-то функцию (например, str:replace ()), но импорт проходит неправильно или неполностью (например, теряется пространство имён), а вы затем вызываете эту функцию, то какое-либо сообщение об ошибке не выдаётся. Функция просто проводит перерасчёт со своим последним аргументом. Как это могло когда-либо быть желательным поведением? Если я вызываю какую-то функцию, которая по каким-то причинам отсутствует, то, ясно, что это всегда ошибка программиста и должно появиться предупреждение.

• Нет способа создать пользовательский набор значений и затем выполнить итерации по нему во время выполнения программы, несмотря на то, что есть способ создать единственное пользовательское значение и затем работать с ним во время выполнения программы. Другими словами, этот язык не имеет типа list/array/tuple/dict/hash/set/iterable/collection.

• Он позволяет использовать '-' в идентификаторах, однако синтаксический анализатор недостаточно проработан для понимания, что вы имеете в виду 'minus' вместо — . Если вы собираетесь разрешить использовать '-' как символ идентификатора и как оператор, то, по крайней мере, следует сделать так, чтобы строка за символом идентификатора '-' соответствовала требованиям к стандартному шаблону идентификатора, [a-zA-Z_][a-zA-Z0-9_]*. Не делайте это использование пробела значащим в языке, где пробел, как правило, не имеет значения вокруг операторов. Никто никогда не пожелает для переменной имя вроде $foo-100, поскольку это очень похоже на $foo — 100.

$foo-bar справедливо интерпретируется как имя переменной.

$foo - 100 справедливо интерпретируется как вычитание.

$foo+100 и $foo + 100 справедливо интерпретируется как сложение.

$foo-100 неправильно интерпретируется как имя переменной.

• Концепция типов, вообще, отсутствует. Всё в основе своей является строкой. Это значит, что даже те элементы, которые по существу разделены на типы, обрабатываются принципиально как строки. Например, сортировка по номеру подчинённых узлов происходит по строковой последовательности, а не по числовой, несмотря на то, что счёт является по сути числовой операцией.

<xsl:sort select="count(*)"/>
<!-- sorts like this: 10, 11, 1, 23, 20, 2, 37, 33, 31, 3, 4, 5, 6, 78, 7, 9 -->

• Имеется слишком много уровней синтаксической и семантической интерпретации:

1. Анализ синтаксиса XML (обеспечивает, что все узлы закрыты и т.п.).
2. Анализ синтаксиса XSL (обеспечивает, что узлы, которые должны быть под другими узлами и/или должны содержать другие узлы, присутствуют, проверяет, что все имена узлов xsl:foo являются действительными и т.д.).
3. Анализ семантики XSL (находит правильные атрибуты для каждого типа узла и т.п.).
4. Анализ синтаксиса XPath (полностью содержащийся внутри значений атрибутов, анализ не может быть проведён ранее).
5. Анализ семантики XPath.

Почему CSS отстой


• Почему есть hsla(), но нет hsva()?

• text-align:justify;, на самом деле, означает «выравнивание по левому краю». Нет способа выровнять по правому краю или по центру.

• vertical-align:middle; не работает с элементами блока, хотя работает со строковыми и табличными элементами. Это ведёт к тому, что люди предлагают display:table; и display:table-cell;, а это значит, что необходимо придавать стиль также упаковщику. Ничего себе!

• Не предполагалось поддерживать горизонтальное выравнивание элементов блока и может быть сделано, в лучшем случае, лишь хакерским приёмом (margin: 0 auto;).

• Можно поместить элемент слева или справа. Но невозможно поместить элемент в центре.

• float: только по горизонтали; нет эквивалентной вертикальной операции. (ОК, это, на самом деле, операция потока, пойди разберись.)

• Аналогично нет вертикального эквивалента для clear:.

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

• Нет способа агрегировать или программно создавать длину. CSS3 вводит calc в значения CSS (CSS Values) и модуль единиц (Units Module), но невозможно задать что-нибудь вроде { width:50% — 2px; }.

• Спецификация CSS является противоречивой в отношении идентификаторов:

• Синтаксис утверждает, что идентификаторы не позволяют использовать символы верхнего регистра везде, кроме первого символа:


• ident {nmstart}{nmchar}*
• nmstart [a-zA-Z]|{nonascii}|{escape}
• nmchar [a-z0-9-]|{nonascii}|{escape}

• В разделе «4.1.3. Символы и регистр» сказано:

«Согласно CSS2 идентификаторы (включая имена элементов, классы и идентификаторы в селекторах) могут содержать только символы [A-Za-z0-9] и символы ISO 10646 161 и выше, а также дефис (-); они не могут начинаться с дефиса или цифры.»

Почему понадобилось отклониться от стандартного формата идентификатора, [a-zA-Z_][a-zA-Z0-9_]*, который использовался с 1970-х годов?

• У нас когда-либо будут альфа-маски? Webkit делает это, но…

• С поддержкой префиксов поставщика дело обстоит неважно. Претендующий на это — Webkit — является единственным префиксом поставщика, и с ним совсем плохо.

• Имеются SASS, LESS и Stylus. Возьмите каждую отдельную характеристику. Каждая является CSS wtf (хотя синтаксис, базирующийся на отступе, должен быть опциональным).

• Нет селекторов родительских элементов, даже в CSS3. Может быть, это будет, в конце концов, завершено в CSS4.

См. также «CSS выглядит неэлегантно» и «Неполный список ошибок, сделанных при разработке CSS».

Исправлено в CSS3

• Привет? Закруглённые углы? Mac OS имел их ещё в 1984. (CSS3 ввёл border-radius:)

• Может быть задано только одно фоновое изображение. (CSS3 поддерживает несколько фонов)

• Невозможно задать растягиваемые фоновые изображения. (CSS3 вводит background-size: и background-clip:)

• Невозможно подготовить вертикальный или наклонный текст. (CSS3 вводит rotate)

Почему Scala отстой


• Иерархия scala.collection слишком сложная.

• Отсутствуют общие функциональные структуры вроде Monad и Functor. Хотя Scalaz обеспечивает большинство требуемых классов.

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

• Чистота функций не может быть проверена на соответствие типов.

• Слишком много способов сделать всё.

Почему Haskell отстой


• Чрезмерная увлечённость короткими и произвольными именами (даже символы) для всего. Программисты на Ассемблере и С не имеют ничего против Haskell из-за этого, потому что при коротких именах программы больше походят на математические выкладки. Очевидно, это характеризует положительно соглашения о программировании, а не означает что-либо плохое о соглашениях по математическим выражениям.

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

• Неприятная система типов.

• Есть на самом деле специальная поисковая система Haskell, чтобы искать что-либо связанное с Haskell, поскольку из-за необычного синтаксиса возникают проблемы с Google.

• Haskell позволяет очень легко написать блоки кода, которые будут семантически идентичными, но по вычислениям различающимися. Эта проблема остро стоит со сторонними библиотеками.

Почему Clojure отстой


• Синтаксис Lisp не позволяет видеть, что представляет собой функция и т.д. — Нет визуального различия.

• Длительный запуск делает почти невозможным написание достаточно производительной программы командной строки.

• Функция Conj действует несообразно в зависимости от предусмотренного типа (присоединяет к началу векторов и заголовку списков).

Почему Go отстой


Базовые средства программирования (базовый язык)

• Go поддерживает пустой («nil») указатель. Это похоже на void * в С — чудовищный источник ошибок. Поскольку «nil» («ноль») может представлять любой тип, то это полностью разрушает систему типов.

func randomNumber() *int {
  return nil
}

func main() {
  a := 1
  b := randomNumber()
  fmt.Printf("%d\n", a+*b) // Замешательство при выполнении из-за нулевого (пустого) указателя
}

• Поскольку строки представляют собой просто последовательности байтов, не существует простого способа индексации или секционирования строки, если она содержит не-ASCII символы. Необходимо перевести строку в последовательность «рун» (кто же умудрился так назвать эти символы?) и затем проиндексировать полученную последовательность.

• Несмотря на вышесказанное, Go имеет два конкурирующих типа для представления текста — string и []byte. Тип string намного превосходит тип []byte, так как он даёт символы Unicode при итерации, при нём можно проводить сравнение с использованием == > <, проводить слияние с использованием + и применять символы как условные обозначения. Однако важные стандартные интерфейсы, как, например, io.Writer, используют []byte.

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

• Хотя Go не требует наличия break в конце каждого case, оператор break всё же выделяется из операторов switch. Та же логика всегда может быть реализована другими структурами управления, но необходимо использовать маркированное прерывание и продолжать выходить из цикла, находясь внутри switch.

• Удаление элемента «n» из последовательности не похоже на удаление: slice = append(slice[:n], slice[n+1:]...)

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

• Составные функции, возвращающие несколько типов, вносят путаницу.

• Тип ошибки Go является просто интерфейсом для функции, возвращающей строку.

• В Go отсутствуют типы сопоставления с образцом и абстрактных данных.

• В Go отсутствуют неизменяемые переменные.

• В Go отсутствуют обобщённые типы.

• В Go отсутствуют исключения, а вместо этого везде используются коды ошибок, что приводит к повторяющемуся шаблону контроля ошибок:

if v, err := otherFunc(); err != nil {
  return nil, err
} else {
  return doSomethingWith(v), nil
}

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

• Go-цикл range итерирует по коллекции, назначая свои ключи и/или значения некоторому количеству переменных, но количество разрешённых переменных и их значения зависят от типа коллекции, который может быть не прописан в исходном коде:

d := loadData()

for i := range d {
  // если "d" является каналом, то "i" является элементом, считываемым из канала
  // если "d" является картой, то "i" является условным обозначением
  // иначе "i" является индексом массива/совокупности/строки
}

for i, j := range d {
  // если "d" является каналом, то это неправильно!
  // несмотря на то, что при нормальном получении из канала, можно получить второе логическое значение,
  // показывающее, закрыт ли он)
  // если "d" является строкой, "i" - индексом, а "j" - руном (необязательно d[i])
  // иначе "i" является индексом массива/совокупности или условным обозначением, а "j" является d[i]
}

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

func foo1(i int) (r int) {
  if i != 0 {
    r := 12
    fmt.Println(r)
  }
  return  // возвращает 0
}

func foo2(i int) (r int) {
  if i != 0 {
    r = 12
    fmt.Println(r)
    return  // возвращает 12
  }
  return  // возвращает 0
}

func foo3(i int) (r int) {
  if i != 0 {
    r := 12
    fmt.Println(r)
    return  // ОШИБКА: "r" перекрыто при возврате
  }
  return
}

• Задержанные функции могут выполнить запись в возвращаемые значения, имеющие имена функций, что может привести к неожиданностям:

func dogma() (i int) {
  defer func() {
    i++
  }()
  return 2 + 2
}

func main() {
  fmt.Println(dogma) // печатает 5
}

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

func returnsError() error {
  var p *MyError = nil
  if bad() {
    p = ErrBad
  }
  return p // Будет всегда возвращена ошибка "не ноль".
}

Взаимосовместимость

• Если имеется несколько каналов для приёма или отправки, то оператор select выбирает case случайным образом, а это означает, что для обеспечения приоритета одного канала над другим необходимо написать:

select {
case <-chan1:
  doSomething()
default:
  select {
  case <-chan1:
    doSomething()
  case <-chan2:
    doSomethingElse()
  }
}

• Оператор select реализован в виде примерно 700 строк исполняемого кода. Можно даже ощутить снижение производительности при каждом его использовании.

•Утечки памяти в стандартных Go-программах, когда Go-программа теряет все свои пути коммуникации с другими, могут привести к потере всей «стековой» памяти, для которой не может быть автоматического управления освобождением динамической памяти.

Стандартная библиотека

• Строки формата даты/времени не используют тип мнемонических кодов, имеющийся в других языках, вроде «ddd» для сокращённого дня или "%H" для часа. Вместо этого библиотека Go использует систему, где «1» обозначает месяц, «5» — секунды, «15» — час, «6» — год и т.д. Документация объясняет это в терминах магического отсчета времени (пн 2 янв 2006 15:04:05 MST) и говорит: «Чтобы задать свой собственный формат, запишите, как будет выглядеть опорная точка отсчёта времени, отформатированная вашим способом». Но это на самом деле не разъясняет, как преобразовать время, чтобы удовлетворить ваши требования; здесь просто признаётся фиксированный, недокументированный набор кодов.

• Две реализации случайных чисел — math/rand и crypto/rand.

• Пакет flag, который реализует флажки командной строки, не является POSIX-совместимым и не поддерживает сокращённые флажки.

• Пакет errors занимает двадцать строк, т.к. тип ошибки Go является просто интерфейсом для функции, возвращающей строку.

Набор инструментальных средств

• Собственная пакетная система Go не поддерживает специфицирующих версий или фиксаций в информации о взаимозависимости. Взамен Go-сообщество рекомендует иметь для каждого главного релиза свой собственный отдельный repository; github.com/user/package/package-{v1,v2,v3}.

• Собственная система пакетов Go не поддерживает зависимости, вынуждая сообщество создать альтернативы.

• Версия 1.5 преобразует весь набор инструментальных средств из C в Go. Поскольку это выполнялось в основном автоматически, то производительность существенно снижалась как при компилировании, так и при работе.

• Компилятор имеет опцию "-u", задокументированную как «отвергнуть небезопасный код». Всё, что она делает, препятствует импорту unsafe пакета или любого пакета, который импортирует его рекурсивно, что включает в себя практически всю стандартную библиотеку; это делает любую программу, компилируемую с указанной опцией, неспособной к выводу и таким образом бесполезной.

• У компоновщика есть такая же опция, только она предотвращает компиляцию *любой* программа, поскольку рабочий цикл Go зависит от unsafe.

Сообщество

• Большинство выражений протеста, особенно по обработке ошибок, отметается как «вы недостаточно хорошо понимаете этот язык».

• Например, данная публикация была закрыта разработчиками немедленно как «WONTFIX» («Проблема есть, но решаться не будет»), несмотря на большое число пользователей, поддержавших её.

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

• Почти ничто в этом списке не может быть исправлено из-за обещания совместимости Go 1. Это значит, надо ждать Go 2.0, но он может и не придти.

• Участники нарушили это обещание в версии 1.4, отвергнув двойные разыменовывания.

Почему Rust отстой


Безопасность

• borrowck отвергает безопасный код.

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

• borrowck, всё же, имеется, даже когда вы находитесь внутри unsafe {} блока или функции; чтобы сотворить, действительно, волшебство, необходимо дать вызов, содержащий некоторые многословные имена вроде sliceable.unsafe_get(index). К тому времени, как вы закончите писать, вы получите своего рода текстовый эквивалент дымовой пожарной сигнализации, срабатывающей, когда ваш тост уже сгорел.

• Никто не знает, какие правила определяют опасность.

• Если вы думаете, что знаете их, то, может быть, присоединитесь к [команде, обсуждающей правила определения небезопасности]?

• Это означает, что содержимое небезопасного блока является трудным для чтения, что оно по определению является слишком сложным, чтобы быть очевидно правильным, что оно нечётко задано и должно быть написано на коде низшего уровня. Удачи — и топайте дальше, если что-то получите не так, как это было у С-программистов последние двадцать лет!

• У Rust есть исключения. Он называет их тревожными и редко использует их, но у него они есть, и ваш небезопасный код должен быть транзакционным в отношении безопасности памяти. До сих пор они имели примерно такой же успех, последовательно соблюдая это, какой имели разработчики в C++ (за исключением тех разработчиков в C++, которые запрещают исключения, как, например, Google).

• Оптимизатор LLVM рассматривает неопределённое поведение как лицензию на убийство. Очевидно, что это имеет значение только для небезопасного кода, но прокрутите текст назад, если вы думаете, что «да пошли вы все, кто использует небезопасный код,» является оправданием. И это при условии, что оптимизатор не имеет ошибок, что в действительности не так.

Очевидно, что из-за проблемы останова не представляется возможным исключить программы, которые делают определённые вещи, не исключая также программы, которые на самом деле не делают их. Однако интересно, что есть такое твёрдое правило об определённых видах ошибок, которые не являются даже худшими видами имеющихся ошибок. SEGV намного лучше, чем CRIME, и Rust не мог бы предсказать это. Я предлагаю прочитать статью Даниэля Дж. Бернштейна о том, почему оптимизирующие компиляторы для безопасных относительно памяти языков являются неправильным решением.

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

Синтаксис

• Точки с запятой и неприятный синтаксис :: получены в наследство от С++. Также унаследован ещё более безобразный шаблонный/универсальный синтаксис.

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

  #[allow(unrooted_must_root)]
  pub fn new(localName: Atom,
             prefix: Option<DOMString>,
             document: &Document) -> Root<HTMLIFrameElement> {
      let element = HTMLIFrameElement::new_inherited(localName, prefix, document);
      Node::reflect_node(Box::new(element), document, HTMLIFrameElementBinding::Wrap)
  }

  #[allow(unrooted_must_root)]
  pub fn new(localName: Atom,
             prefix: Option<DOMString>,
             document: &Document) {
      let element = HTMLIFrameElement::new_inherited(localName, prefix, document);
      Node::reflect_node(Box::new(element), document, HTMLIFrameElementBinding::Wrap);
  }

• Чрезмерно кратко поименованные типы и ключевые слова, которые не передают их назначение, как, например, impl и ().

mut означает исключительный, а не изменчивый (mutable). &Mutex означает изменчивый (mutable), а &mut str — неизменяемый (immutable).

Конструкция API и система типов

• Чрезмерно кратко поименованные типы и ключевые слова, которые не передают их назначение, как, например, Vec и Cell.

• Большинство думает, что Rust имеет два типа строк (и это непохоже на С++, где имеется один хороший тип и один тип, унаследованный от С; поддержка обеспечена для обоих — как для str, так и для String). Здесь их фактически шесть: str, String, CStr, CString, OsStr и OsString. Нет независимого от платформы способа преобразования друг в друга OsStr, OsString, CStr, CString (из-за Windows), но есть много слегка различающихся способов преобразования CStr, CString, OsStr, OsString в String и str, и есть способы преобразования в другую сторону.

• Повсеместное неявное преобразование (типа) означает, что справочные документы практически бесполезны. Можно взять Vec<T>, поскольку он неявным образом преобразует в &[T], например, и можно использовать для циклов с Vec<T> и &[T], но результат будет немного различающимся в зависимости от того, что используется.

• Не совсем повсеместное преобразование (типа) означает, что ваш код оказывается замусоренным тарабарщиной вроде &*some_var (который преобразует интеллектуальный указатель в ссылку) и &some_var[..] (это — та самая магическая формула, которая преобразует Vec<T> в &[T] или String в &str).

• Дублирующие элементы, такие как, например, структуры кортежа и модулеподобные структуры (учтите, что сам модуль, который на самом деле является () по некоторой причине, есть кортеж, а не модулеподобная структура).

• В системе типов имеется гражданин второго сорта в форме не-Sized типов. Примеры — str и [T]. В отличие от любой другой характеристики в этом языке, каждый обобщённый тип требует Sized, если вы не отказываетесь, выставляя требование ?Sized. Эти типы настолько второсортны, что народ на странице Reddit, вообще, не признаёт str типом. Скажите это моей Box<str>. И это вовсе не ограниченные безответственные посетители сайта Reddit; авторы Reddit, которые достаточно подготовлены, чтобы знать о взаимосвязи между проблемой останова и граничным умозаключением, неправильно понимают семантику не-Sized типов.

• Типы суммы — не enum. Хватит притворяться С.

Сообщество

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

• Это просто сборище «няшек». Вам не позволено неконструктивно критиковать Rust (и, очевидно, что высказывание «концепция, лежащая в основе этого языка, настолько неправильная, что он никогда не может быть исправлен» здесь не пройдёт), но вам одновременно также запрещается называть другие языки за безнадёжно низкое качество. Я надеюсь, что вы способны к двоемыслию, потому что придётся думать, что Rust нуждается в веб-структурах и одновременно не конкурирует с Java!

Набор инструментальных средств

• rustc работает медленно.

Здесь приведены все целевые поддержки rustc. Нет PIC. Нет AVR. Нет m68k. Только ARM, x86, MIPS, PowerPC и NaCl. Все платформы, которые могут легко позволить себе сборщик мусора.

• Изменить файл? Потребуется перекомпилировать всю вашу библиотеку. Кстати — rustc работает медленно.

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

• Действительно, он статически связывает почти всё. Он динамически связывает вашу программу с библиотекой, в результате чего ваши исполняемые файлы не являются, на самом деле, самодостаточными.

• Обновление какой-либо зависимости требует от вас перекомпилировать всё, что связано с нею. Это занимает много времени, поскольку rustc работает медленно.

• Нет пространства имён в поставке. Всё получает «креативные» имена, как, например, «ring» (набор криптопримитивов) или «serde» («serialization and deserialization» («сериализация и десериализация»), ничего?).

• Каждый исполняемый элемент содержит по умолчанию копию менеджера распределения памяти jemalloc, выдающего «hello world» [размер почти мегабайт].

• Поскольку в поставке нет никаких попыток кэширования артефактов компилятора, то вы компилируете каждую раздутую библиотеку каждый раз, когда создаёте новый проект. Я уже говорил, что rustc работает медленно?

• Обобщённые типы очень распространены, и они по существу копируются и вставляются для каждого конкретного типа, с которым они используются (спасибо тебе, С++, что сделал это популярным). Вы знаете то, что делает компиляцию по сути тем же самым кодом часто ещё более болезненной?

• Нет хорошего IDE. Если вы использовали что-либо вроде C# в VS или Java в Eclipse, то получите ощущение старой ржавой вещи из 90-х.

• Почему нельзя иметь IDE, который рекомпилирует проект постоянно, как это делает Eclipse? У меня есть одно предположение.

• Автозавершение опережающего ввода с клавиатуры? Это, действительно, может быть сделано, но возникает ощущение реактивного двигателя, смастерённого из набора «сделай сам». За тем исключением, что если бы этот реактивный двигатель выходил из строя так же часто, как это происходит с Racer, то он никогда не был бы разрешён к использованию Федеральным авиационным агентством.

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

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

    +59
    Баян. Скучно. Придирки к стилю – тупо. Перевод отстой:
    '0', 0 и 0.0 являются неправильными, но '0.0' — правильным.
      +3
      А имеется в виду допустимость этих конструкций или то, как они приводятся к булевским переменным? Не зная джавы, я не понимаю, о чём речь.
        +6
        как они приводятся к булевским переменным

        А, так вот что это значило. В таком виде эта строка обретает смысл :)
          0

          При чём здесь java? Это про js.

            +2

            Причем тут js? Это в разделе про PHP.

              0

              Ок, но это точно не про java, которая упомянута в комментарии, на который я отвечал.

          +8
          Ну нахер! Пошёл доучивать английский, чтобы больше не видеть эти волшебные переводы от надмозгов, научившихся пользоваться PROMT'ом в перерывам между охлаждением трахания и рассматриванием пользы с Углепластиком.
          +15
          Как мне теперь на C# писать с такими чудовищными недостатками? Я ведь так хотел получить исключение в рантайме, просуммировав два объекта, в классах которых не определён оператор сложения.
            +1
            Кстати говоря, со сложением не все так однозначно. Если складывать dynamic, то все будет работать:
            T Plus<T>(T t1, T t2)
            {
                return (dynamic)t1 + (dynamic)t2;
            }
            
              +2

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


              А вообще да, если хочется то через динамик всегда можно.


              Даже мультиметоды (двойную диспечиризацию) можно сделать.

              +6
              Для меня это большая боль — операторы и методы generic'ов. Если бы все int, float и т.п. были бы каким-нибудь Numerable (да, я знаю, что структуры не наследуются), чтобы использовать where, все было бы чуточку проще
                +1
                Если для вас это действительно важно и это не просто «хотелка», то можно сделать метод не параметризованным, а работающим, например, с double и уже результат его работы приводить к тому или иному численному типу с нужными допущениями (округления, отсечение дробной части).
                Можете так же посмотреть на Scala, если у вас есть выбор. Там у операторов нет привелегированного положения и они являются обычными методами, что позволит сделать generic-метод с нужными constraints.
                  0
                  Скорее просто хотелка.

                  Там у операторов нет привелегированного положения и они являются обычными методами, что позволит сделать generic-метод с нужными constraints.


                  Операторы в C# — обычные методы operatorX. Параметр generic-метода без всяких constraints имеет методы Object (ToString, Equal, GetHashCode итп). Сравнения, операции, другие методы — все надо делать через constraints определенного интерфейса. Например, сравнение — IComparable. Но интерфейса для математических операций нет. Да и примитивные типы являются по-сути структурами, а не классами, поэтому даже IComparable тут не канает.

                  P.S. Случайно узнал, что Array.Sort как-то работает даже с T[] без всяких ограничений.
                  –1
                  where T: struct
                  разве не решает проблему?
                    +2
                    Не решает: не всякая структура — число.
                    Максимум, вы можете написать where T: struct, IComparable, но кроме чисел в ограничение впишется и любая другая структура (не число) с заданным интерфейсом.
                      0

                      Можно еще IConvertible дописать. Просто потому что это более редкий интерфейс, нежели IComparable.

                +49
                А я считаю отличный справочник для начинающих троллей. Можно легко начать языковый срач по любому языку из списка. А если серьезно в принципе проделана хорошая работа. Такой общий взгляд как раз показывает, что идеальных языков нет, и они порой имеют похожие болячки. А также поможет не сформировавшим свое мнение особам перестать перебегать с одного языка на другой надеясь что вот следующий язык уж точно будет лучше.
                  +21
                  Да нихрена этот «общий взгляд» не показывает. Возьмите мои любимые .NET и C# – из обоснованных претензий там только IDisposable да property override. А реальные проблемы, вроде бардака в коллекциях, идиотского System.Enum, неудобных делегатов (broken by design!), бредового доступа к памяти и массивам (планируется улучшить в виде Memory Span API), отсутствие tagged union (частично исправлено в C# 7) и др., не упомянуты.
                    +15
                    Да с плюсами то же самое.

                    И про хаскель, неприятная система типов? Серьёзно?

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

                        Что ещё не указано и что напрягает лично меня, например — совершеннейше ужасная работа gc в параллельном режиме. Если запускать программу с +RTS -Nn при n существенно большем единицы, gc начинает жрать почти всё время.

                        Но это всё в любом случае уже скорее quality of implementation, а не непосредственное свойство языка.
                      +1

                      Да там даже IDisposable не в тему. Давно же уже договорились, что при правильной архитектуре полный Disposable-паттерн не нужен.


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

                        0
                        >только управляемыми ресурсами
                        иногда от управляемых ресурсов тоже надо уметь вовремя отписываться
                          0

                          А в чем проблема вовремя отписаться от управляемого ресурса? Для этого не нужны финализатор и полный Disposable-паттерн.

                            0
                            Пардоньте, невнимательно прочитал в первый раз. Претензий больше не имею.
                        +1
                        Сколько использую C#, первый раз слышу про некоторые вещи, можете пояснить?

                        неудобных делегатов (broken by design!)

                        А что в них неудобного? Я особенно сильно страдал, когда пришлось одно время писать на Java и делегаты (и события) просто рай земной.

                        бредового доступа к памяти и массивам (планируется улучшить в виде Memory Span API)

                        А вот это сильно интересно. В чем бред?
                          –3
                          Делегаты – это просто класс с конструктортом и некоторыми методами, который генерируется компилятором и заменяется на специальный код в рантайме средой исполнения. У делегата есть контекст – это ссылка на объект, которая поставляется как первый аргумент методу. За исключением случая, когда контекст равен null, тогда через рефлексию выясняется, как вызывать метод, поэтому делегаты такие медленные. У делегатов может быть не одна пара контекст-метод, а несколько. Делегат без таких пар превращается в null. У делегатов нет структурной эквивалентности и наследования друг от друга, т.е. EventHandler и Action<object, EventArgs> это совершенно разные вещи. Короче говоря, лучше чем в Java, но хак на хаке и могло быть гораздо удобнее.

                          Про доступ к памяти я могу писать бесконечно. Например, почему нет read-only array? Почему String и char[] это разные вещи с разными API? Почему разые API используют массивы, строки, указатели, IntPtr, что угодно из предыдущего плюс start index и length, иногда даже IEnumerable, но не единый способ доступа к памяти? Почему существует ArraySegment, но он нигде не используется? Почему я не могу превратить часть строки в число и обратно без миллиона аллокаций? Почему есть OffsetToStringData, но нет аналога для массивов? (Ответ: потому что массивы это один большой «особый случай» для среды исполнения и языков.) И я ещё не начинал про SZArray! Это всё начинают исправлять с внедрением Memory Span API. Надеюсь они доведут дело до конца и переделают большинство стандартной библиотеки на нормальные аргументы.
                            0

                            String и char[] — это разные вещи потому что первый неизменяем. А StringBuilder и char[] — это ращные вещи потому что у первого переменная длина. Часть строки превращается в число и обратно при помощи всего одной аллокации, а не миллиона.

                              0
                              Ответ ниже.
                              +1
                              Почему String и char[] это разные вещи с разными API?


                              Потому что строки — иммутабельные объекты (поэтому нет методов работы как IEnumerable) и имеют (внезапно) своим методы по работе со строками (не C все-таки).

                              С остальным в принципе соглашусь, но в 95% случаев (на вскидку) используются различные реализации IEnumerable, а точнее — массивы и System.Collections.Generic

                              Надеюсь они доведут дело до конца и переделают большинство стандартной библиотеки на нормальные аргументы.

                              И сломают кучу-кучу-кучу-кучу существующего кода?
                                –2
                                (Вы уж не думаете, что я этого не знаю?)

                                Проблема в том, что различные API требуют char[] или string, хотя им нужно гораздо меньший контракт, чем предоставляют char[] или string. Они вынуждены поддерживать оба класса, потому что для потребления они эквивалентны (даже имеют бинарную совместимость), но типа с подходящим контрактом попросту не существует.

                                Возьмите конструктор строки: вам на самом деле требуется всего лишь указатель, длина, кодировка для создания строки, а не 8 оверлоадов. Это всё можно схлопнуть в два-три варианта конструктора с ReadOnlySpan.

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

                                В 99% случаев, когда используется массив в качестве аргумента, тип массива не подходит. Возьмите класс Array – все методы, кроме Clear, SetValue, Reverse, Sort, не должны принимать массивы в качестве аргументов. И опять ReadOnlySpan спасет.

                                А ещё поддержку UTF-8 строк мешает сделать именно вот такой изначально плохой подход к работе с памятью. Основной вариант сейчас это распознавание отдельных опкодов в существующих программах и изменение их для более корректной работы с памятью.
                                  –1
                                  Проблема в том, что различные API требуют char[] или string, хотя им нужно гораздо меньший контракт, чем предоставляют char[] или string.

                                  Куда уж меньше char[] ?

                                    –1
                                    ReadOnlySpan.

                                    Судя по минусам, хабравчане считают себя умнее разработчиков .NET, больше не буду на эту тему комментировать.
                          +9
                          Скорее такие списки рождают гениев, которые в интернетах потом на полном серьезе доказывают, что ваш язык говно, потому что какие-то ноунеймы в список добавили всякий рандомный хлам. Это все шутки и вбросы, ничего более.
                            +35
                            Попытаюсь пошутить.

                            Как выбирают
                            Javascript: И фронт и бек на одном языке, ну наконец то.
                            PHP: В моем городнейм 120 вакансий ит, 110 по php, без хлеба с маслом не останусь.
                            Java: СМАРТФОНЫ = ANDROID! ANDROID = СМАРТФОНЫ!
                            Obj-C: Я что зря накопил на макбук и айфон.
                            С++: ОБОЖАЮ ИГРЫ. Отправил резюме в еа, убисофт, и т.д.
                            C#: Прикинув силы реально, попробую на Unity.
                            С: Я использую Линукс, или как я недавно начал говорить Гну слэш Линукс.

                            Как не выбирают язык
                            pony() + (!((f{}=try(a(a+b==(!x.magic%n)))/(/friend/*)::bool(1)))) // Вот так не работает, а ведь должно!
                              0
                              Замечательные примеры!

                              По основной статье — справедливости ради отсюда бы поубирать придирки к стилям именования а также болячки ранних версий языков. У того же Руби в 2 раза тогда сократится список «болячек». На самом деле, какая разница что было в 2007-м с языком, если сейчас в том же 2.3.1 большая часть претензий уже не актуальная. Что касается придирок по upcase и downcase — вообще вкусовщина странная. Если уж так нужно, руби позволяет применять манки-патчинг к кор-методам и классам, так что при желании за пару строк можете себе сделать хоть метод toUpperCaseForMePlease и это будет работать. А если совсем тяжелый случай — оформляете эти горе-методы в свою либу (тоже всего несколько строк) и инклудите в свои проекты (главное чтобы на гитхабе не увидел никто). В свое время поступал так с округлениями при переходе с 1.9 на 2.0, писал манки-патч умещающийся в один 6-ти-строчный гист.
                              +9
                              Очень много вкусовщины, причём необоснованной. CamelCase — отстой? Ну ок, а какие ваши аргументы? И на этом начинающий тролль всё.
                              И много фактических ошибок. Мелких, но показывающих поверхностное знание предмета.
                              Например: «любой php код, даже не генерирующий html, должен быть заключен в <?php… ?> » — закрывающая скобка мало того, что необязательна, так ещё и обоснованно считается довольно серьёзной ошибкой.
                                +2
                                С CamelCase, я так понял, претензия к сложным именованиям — XMLHttpRequest (я бы написал XmlHttpRequest, например) и HTMLHRElement (ну тут действительно волшебно — HTML HR и Element сливаются… не айс… лучше бы было бы HtmlHrElement)
                                0
                                это потому что твой язык — отстой, ты говоришь что идеальных языков нет? :-\
                                +1
                                isNaN(x) — хоть и преведено как «правильный способ тестирования» на самом деле таковым не является. Проверяет не на то является ли данный объект NaN а на то получится ли из этого объекта NaN при приведении к числу

                                для примера

                                    isNaN('asd')  //вернёт true, хотя 'asd' это строка а не NaN
                                    Number.isNaN('asd') //вернёт false, что на мой взгляд более верно
                                
                                  0
                                  Is 'asd' not a number?
                                    0
                                    'asd' is not a number, but it's not a 'NaN'
                                      +1
                                      Все верно.
                                      isNaN(s) проверяет является ли s «не числом» явно или не явно (в виде строки)
                                      Number.isNaN(s) проверяет является ли s именно значением NaN
                                      Обе функции дают правильный результат, но смысл у них немного разный.
                                        0
                                        Я знаю как работают эти функции, именно поэтому я написал этот код. Просто функции isSomething обычно возвращают true если переданный аргумент является этим Something. Строка NaN-ом не является. Строка это строка.
                                          0
                                          Все началось с шутки в которой была лишь доля шутки. Не хочу дальше спорить.
                                            0
                                            Да я то даже и не спорю, просто isNaN считается примером нехорошей части javascript, т.к. делает не то, как называется
                                    +3
                                    «NaN» это «Not a Number».
                                    Выражение if isNaN(a) соответствует «если a не число», что логично, на мой взгляд.
                                      0
                                      Значит вы просто не знакомы с IEEE754
                                        0
                                        Для тех кто хочет смысл именно IEEE754 есть Number.isNaN.
                                          0
                                          Это очень плохая практика, называть что-то так, что это можно трактовать несколькими способами. Все должно быть однозначно. Термин NaN — из IEEE754. И не надо ему придавать других значений.
                                          Ваша задача не соблюсти формальность, а сделать так, чтобы программист, использующий вашу библиотеку не ошибся по той или иной причине. Ваши оправдания никак этой цели не соответствуют.
                                          Более того, не рекомендуется использовать отрицание в названии функции. Лучше сделать функцию isNumber().
                                            0
                                            Вот чтобы такой ерунды не возникало, и нужна строгая типизация.

                                            Может, это правильно, когда при попытке вызова isNan(string) язык возвращает не true и не false, а exception?
                                              +1

                                              NaN в данном контексте ни разу не отрицание, это просто название специального значения. Для isNumber было бы ожидаемо давать ответ на вопрос «тип аргумента — число?» и isNumber(NaN), соответственно, должно выдавать true: NaN в системе типов это число.

                                                0
                                                Почитайте выше: https://habrahabr.ru/post/315152/#comment_9914104
                                                В данном контексте это как раз не «специальное значение NaN». Оттого и спор.
                                                  0

                                                  Даже в данном контексте это «специальное значение». isNaN(x) — это как Number.isNaN(+x), Number.isNaN(x) — это typeof x === 'number' && isNaN(x). Что‐то вроде предложенного ниже convertsToNaN было бы, конечно, более однозначным названием, но это всё ещё проверка на то, не является ли x специальным значением, хотя и в контексте, в котором x будет приведено к числу. Никакого отрицания тут нет, потому что NaN — тоже число согласно системе типов. Отрицание было бы, если бы при приведении генерировалась ошибка, и тогда название isNaN было бы весьма странным.

                                                    0
                                                    Просто когда наша переменная NaN мы ничего сделать не можем, кроме как залогать ошибку\бросить exception, а когда это строка не приводимая к числу — вполне может быть что там что-нибудь внятное, просто необработанное. И вот тут то из-за неоднозначного названия может закрасться логическая ошибка.
                                                      0

                                                      С тем, что названию лучше бы быть другим я согласен. Но с тем, что «другим» значит !isNumber — нет, равно как и с тем, что в названии isNaN есть отрицание. В частности, convertsToNaN было бы адекватнее, но, вообще‐то, пока унарный плюс не бросает исключений (кстати, а он при каких‐то условиях бросает, при которых isNaN — нет?), ни isNaN, ни convertsToNaN не нужны при наличии Number.isNaN.

                                                        0
                                                        Да, просто нужно знать про Number.isNaN, который кошерен. И понимать что он отличается от isNaN. Не читавшему доки может быть открытием.
                                          0
                                          if isNan(a) на мой взгляд должно означать что a это NaN
                                            –3
                                            Что а, приведённое к числу — это NaN.
                                              0
                                              тогда функция должна называться convertsToNaN, или как-нибудь ещё, но is подчёркивает что переменная в данный момент является NaN
                                                0
                                                Так isNaN принимает на вход число, а дальше в силу вступает неявное приведение типов. Сама функция тут ни при чём, иначе все функции в JS придётся называть «convertXX».
                                                  0
                                                  javascript же не строготипизированный, так что isNaN принимает переменную, и независимо от типа переменной должен вести себя правильно. Тоесть если пришло не число — нужно возвращать false, т.к. typeof NaN === «number»
                                                    –2
                                                    Он ведёт себя правильно, вас же 10+true === 11 не смущает?
                                                    В JS вообще функции в первую очередь реализуются с неявным приведением (==, и только потом ===), а большинство и вообще не имеет аналогов без приведения. «false».endsWith(false) === true.
                                                      +1
                                                      Должна ли функция isString приводить к строке входящий параметр?
                                                        0
                                                        Если бы проблемы о которой я говорю не существовало — не появился бы «правильный» Number.isNaN
                                                          –1
                                                          Ну, эти «is» методы Number — это просто аналоги обычных с проверкой типа.
                                                          Так же и isFinite(«123») === true, a Number.isFinite(«123») === false.
                                                          Называть это проблемой — преувеличение. И эти пара добавленных функций — просто сахар, избавляющий от проверки типа, если(!) для вас важно выделять именно NaN, который в вашем коде каким-то образом может приходить вперемежку со строками.
                                                          typeof(a) === "number" && isNaN(a)

                                                          против
                                                          Number.isNaN(a)
                                                          +1
                                                          Судя по названию, она должна проверять тип, а не значение, как делает isNaN.
                                                          Вы же не с (тоже предполагаемой) функцией „isNumber“ сравниваете.
                                                          Хотите проверять без приведения? Пишете x!=x, экономите четыре символа.
                                          0
                                          Эка подрывает, по Go не правда, есть константы
                                            +59

                                            Ваш переводчик — отстой.

                                              +9
                                              Какой-то слабый троллинг, очень слабый троллинг!..
                                                +32
                                                Есть попытка, но не окончательно

                                                Не сразу понял, что речь о try и finally.

                                                  +18
                                                  А как вам понравилось «Обязательным является свой аргумент в методах»? (Речь об аргументе self)
                                                    +2
                                                    Очень долго пытался понять, по каким признакам аргумент становится «своим». Потом дошло, о чем речь
                                                      +10

                                                      Короче сразу минус такому переводу. Непонятно, кто переводил и знает ли вообще переводчик о чём речь в тексте.

                                                    0
                                                    Обязательным является свой аргумент в методах

                                                    Тоже хорошо. Где-то был self.

                                                      +4
                                                      А я вообще, пока не прочитал Ваш комментарий, не понял о чём речь.
                                                        +6
                                                        «Она со мной, углепластик. Охладите траханье»
                                                        +14
                                                        Натяжка на натяжке. Скучно. А, да, чуть не забыл — Delphi рулит.
                                                          –4
                                                          Delphi это не язык
                                                              0
                                                              Wikipedia does not have an article with this exact name.
                                                              MacIn, все же, наверное, имел ввиду Object Pascal в Delphi IDE
                                                                +1
                                                                Попробуйте с закрывающей скобочкой.
                                                                https://goo.gl/5qAzXE

                                                                В любом случае, получилось очень колко.
                                                                  0
                                                                  в адрес нужно добавить закрывающую скобку
                                                                    +1
                                                                    Delphi — это отдельный диалект Object Pascal'я.
                                                                      0
                                                                      Как юнити — это отдельный диалект C#?
                                                                        –1

                                                                        Как Оберон — отдельный диалект Object Pascal.

                                                                          –1

                                                                          Оберон намного сильнее отличается от Паскаля, нежели Delphi.

                                                                            +2

                                                                            Но наверняка Delphi отличается от Паскаля намного сильнее, чем C# от C# :)

                                                                              –2

                                                                              Но не сильнее чем C# от Visual Studio!

                                                                                +1

                                                                                С# всё-таки язык программирования, а Visual Studio — среда разработки. А Delphi c Pascal оба — языки программирования.


                                                                                Эдак можно договорориться до того, что С# от Ворда отличается сильнее, чем Delphi от Pascal :). И, что характерно, утверждение будет верное :)

                                                                                  –2
                                                                                  C# от ворда — сильно, но вот Visual Basic… С чем бы мы его не сравнивали, получается боль…
                                                                                    0
                                                                                    А что не так с visual basic? Полная копия C# — просто синтаксис на другой лад. И компилируется в один и тот же код.
                                                                                      0
                                                                                      Среда, которая чаще всего используется. Программировать дефолтным редактором офиса…
                                                                                        +1

                                                                                        Может вы перепутали с VBA?

                                                                                          0
                                                                                          Допускаю, ибо дело было ещё в школе. И с тех пор редактор в офисе не изменился.
                                                                                            0

                                                                                            А зачем ему меняться, если он как раньше VBA редактировал, так и сейчас?


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

                                                                                              0
                                                                                              Что значит отдельным? Почитайте про то как работает .NETи как все .NET языки «отличаются».
                                                                                                +1

                                                                                                При чем тут вообще .NET? VB.NET — это третий язык, отличающийся как от VB, так и от VBA

                                                                                                  0

                                                                                                  Это всё MS виновата… переиспользуют названия, а потом приходится новичкам по 10 раз рассказывать чем отличается VB от VB.NET, ASP от ASP.NET, ASP.NET от ASP.NET MVC и т.д.

                                                                                                    –1
                                                                                                    Спасибо, не знал о чистом VB.
                                                                    +4
                                                                    Начиная с Delphi7 (2002 год) Борландцы назвали Delphi самостоятельным языком, основанным на Object Pascal.
                                                                      0
                                                                      Странно утверждать такие вещи, не разбираясь в вопросе.
                                                                    +28
                                                                    Есть желание использовать С11 в переносимом виде? Конечно, но в значительной степени ничто, кроме GCC и шумихи, не поддерживает это.

                                                                    Шумихи, #!@^%$.

                                                                    Чем эта статья лучше ссылки на гугл.транслейт оригинала?

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

                                                                    Хотя не, спасибо, тут я вслух засмеялся.
                                                                      0
                                                                      Судя по тому, что Swift обошли стороной, мой язык, всё-таки, не отстой.
                                                                        +31
                                                                        Нужен последний пункт для остальных языков:

                                                                        «А твой любимый язык настолько отстой, что о нем даже писать неохота, это очевидно!»
                                                                          –11
                                                                          Во-во-воу! Не трогай BrainFuck, он охуенен!
                                                                          0

                                                                          Судя по


                                                                          Несовместимость браузеров Firefox, Internet Explorer, Opera, Google Chrome, Safari, Konqueror и т.д. делает работу с DOM чрезвычайно трудным делом.

                                                                          статья писалась тогда, когда Swift ещё не было или он только-только появился.

                                                                            0
                                                                            судя по упоминанию питона 3.5, ес6 — всё же был
                                                                              0

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

                                                                              –1
                                                                              Я это приложение вообще не понял, что значит Несовместимость браузеров, несовестимость с чем, с DOM?
                                                                                0

                                                                                Имеется ввиду, что в браузерах разное API для работы с DOM.

                                                                                  –1
                                                                                  Ага, уже глянул, там как раз чёрным по белому «incompatibilities between».
                                                                                0
                                                                                Упоминание о Swift в статье всё-таки есть (в разделе про ObjC).
                                                                                +1
                                                                                Есть два типа языков программирования — те, которые все ругают, и те на которых никто не пишет.
                                                                                +1
                                                                                А я знаю кобол, его в списке нет, кобол — не отстой.
                                                                                  +1
                                                                                  > Нет делегатов; каждый раз, когда нужен указатель на функцию, приходится реализовывать заводскую разработку.
                                                                                  Не страшно, в java 8 можно делать так:
                                                                                  public interface DelegateInterface {
                                                                                      void run(final String a);
                                                                                  }
                                                                                  
                                                                                  public final class Test{
                                                                                  
                                                                                      public void func(final String param) {
                                                                                          System.out.println(param);
                                                                                      }
                                                                                  
                                                                                      public void test() {
                                                                                          DelegateInterface delegate = this::func;
                                                                                          delegate.run("some text");
                                                                                      }
                                                                                  }
                                                                                  


                                                                                  >Нет буквенных констант для карт или массивов. Массив и карта являются интерфейсами коллекций.
                                                                                  Это о чем?
                                                                                    +1

                                                                                    Это о Array и Map, видимо.

                                                                                      +1
                                                                                      Сквозь этот бред прослеживается боль в отсутствии синтаксического сахара для доступа к элементам Map или Collection.
                                                                                        0
                                                                                        Ага, там речь про literal constants.
                                                                                        0
                                                                                        Лично меня, если честно, бесят вот эти модификаторы перед каждым объявлением — «public», «final» или там «static». Я понимаю, зачем они. Просто после c#, где как-то без этого можно обойтись, не потеряв в функционале, глаза немного кровоточат: конкретно код занимает места столько же, сколько декларации классов и их членов…
                                                                                          0

                                                                                          Да ну? В C# отличается разве что область видимости по умолчанию для членов классов — закрытая вместо внутренней. И внутренних классов (в понимании Java) нет.


                                                                                          В остальном — все также надо расставлять модификаторы.

                                                                                            0

                                                                                            Или вы про final перед параметрами? Так они и в Java не обязательны...

                                                                                            +2
                                                                                            >Нет буквенных констант для карт или массивов. Массив и карта являются интерфейсами коллекций.
                                                                                            Это о чем?


                                                                                            Это о том, что за такие переводы убивают.
                                                                                            +1
                                                                                            Не очень понял каким вообще боком CSS относится к "языкам программирования", но именно раздел с CSS выглядит как жалобы типичного неосилятора.
                                                                                              +13
                                                                                              Ты не поверишь. Там все разделы так выглядят.
                                                                                              +11
                                                                                              У Rust есть исключения.

                                                                                              Ну вот, только на ЛОРе закончился срач. :) Нет в Rust исключений. Исключение это механизм сообщения об ошибке, который выкидывает управление из функции по стеку до ближайшего обработчика. Исключение не трогает объекты программы и предоставляет обработчику решать что делать дальше. Паника же — средство контролируемого уничтожения потока, в котором она произошла. Паника вызывает деструкторы. И то, что её можно перехватить и некоторым шаманизмом сохранить поток не делает её исключением. Объекты всё равно придётся создать заново.

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

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

                                                                                                0
                                                                                                Две реализации случайных чисел — math/rand и crypto/rand

                                                                                                Забавно. В основном все прочитал мельком, а за это прям глаз зацепился. Прям выбивается из всей этой вкусовщины и вбросов.
                                                                                                  +2

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

                                                                                                    –2
                                                                                                    Остальные вещи, по крайней мере, еще как-то субъективны. А это похоже вставлено чисто из-за глупости. Знать, что такое RNG вообще, и не понимать, зачем они разные нужны.

                                                                                                    А что действительно непонятно, это причина минусов. Кто-то еще что ли не знает, зачем именно две разных реализации нужны или что?
                                                                                                      0
                                                                                                      Когда вы написали «выбивается из всей этой вкусовщины и вбросов», читающе ваш комментарий люди подумали, что вы этот пункт одобряете. Потому, видимо, и заминусили.
                                                                                                        –2
                                                                                                        Видимо, не поэтому, раз и вам минус влепили. Хабр такой хабр.

                                                                                                        К слову, нет, не одобряю. Напротив, удивился, что кто-то настолько невежственен, что упомянул такую заковыристую вещь как RNG и осмелился упрекнуть в том, что есть две реализации. Первый раз вообще вижу подобный упрек.
                                                                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                                                                    +24

                                                                                                    Что это за высер промпта из начала 2000-х?


                                                                                                    ничто, кроме GCC и шумихи,

                                                                                                    Убило наповал

                                                                                                      0
                                                                                                      Напомнило: http://harmful.cat-v.org/software/operating-systems/os-suck
                                                                                                        +2
                                                                                                        Прочитал список. Поразительно, но RT11 не сосёт. Все на PDP-11/УКНЦ! :)
                                                                                                        +1
                                                                                                        Про Javascript практически каждый недостаток заканчивается тем, что в «ES6 уже исправлено». Только сегодня практически все пишут на ES6, а кто не пишет — очень рекомендую, или сразу на Typescript
                                                                                                          +7
                                                                                                          Насчет «все пишут на ES6» это преувеличение:
                                                                                                          Картинка


                                                                                                          0
                                                                                                          Остаётся один язык — brainfuck. Он не отстой. На нём девелопить будем.
                                                                                                            0
                                                                                                            Вы забыли про Ada
                                                                                                            +8

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


                                                                                                            Также:


                                                                                                            Язык С

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

                                                                                                            Это вранье. Все статические объекты в С инициализируются нулями по умолчанию.


                                                                                                            Язык С++

                                                                                                            Он имеет обратную совместимость с С.

                                                                                                            Нет.


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

                                                                                                            Различия С и С++ — огромны и фундаментальны. С — lvalue-discarding language, C++ — lvalue-preserving language. Уже этого достаточно для того, чтобы заявлять, что любые сходства между этими языками — не глубже уровня сходного синтаксиса.


                                                                                                            Вызов std::string::c_str() требуется для преобразования std::string в char*. Из самого мощного языка мы всегда имели бы полностью принимаемый перегруженный operator const char* () const.

                                                                                                            Косноязычие не позволяет понять, что именно имеется в виду.


                                                                                                            Разработчикам, возможно, придётся побеспокоиться о вопросах оптимизации, таких как, например, объявлять функцию inline (встраиваемой) или нет; но после принятия такого решения оно является только предложением: компилятор может решить, что предложение неправильное, и не принять его. В чём смысл? Должны ли разработчики беспокоиться о вопросах оптимизации?

                                                                                                            Уже давно было объяснено, что inline больше не имеет отношения к оптимизации. Этот спецификатор влияет лишь на то, как определение функции взаимодействует с One Definition Rule, и не более ни на что. В С++17 появятся inline-переменные, где новая роль inline сияет во всей красе.

                                                                                                              –2
                                                                                                              Это вранье. Все статические объекты в С инициализируются нулями по умолчанию.

                                                                                                              Это реализации, по стандарту инициализация не предусмотрена. Впрочем, может я и упустил нововведения, потому сильно спорить не буду.

                                                                                                              Различия С и С++ — огромны и фундаментальны. С — lvalue-discarding language, C++ — lvalue-preserving language. Уже этого достаточно для того, чтобы заявлять, что любые сходства между этими языками — не глубже уровня сходного синтаксиса.

                                                                                                              Теперь — да.

                                                                                                              Но вообще, для С ниша на сегодня понятна. А С++ — действительно стал монструозен… Но опять же, это сугубо лично моё мнение, и я его ни в коем разе не навязываю.
                                                                                                                0
                                                                                                                Не только ваше мнение, я тоже считаю, что С++ превратился в страшного мутанта.
                                                                                                                В своей работе я использую лишь подмножество С++, наиболее понятное и очевидное. Этим я занимаюсь уже много лет, но ни разу язык не поднялся называть себя программистом С++.
                                                                                                                  +1
                                                                                                                  Нет, неправда. Именно стандарт языка С гарантирует инициализуцию нулями всех статических объектов (неинициализированных явно пользователем).

                                                                                                                  > 6.7.8 Initialization
                                                                                                                  > 10 [...] If an object that has static storage duration is not initialized explicitly, then:
                                                                                                                  > — if it has pointer type, it is initialized to a null pointer;
                                                                                                                  > — if it has arithmetic type, it is initialized to (positive or unsigned) zero;
                                                                                                                  > — if it is an aggregate, every member is initialized (recursively) according to these rules;
                                                                                                                  > — if it is a union, the first named member is initialized (recursively) according to these rules.
                                                                                                                  +1
                                                                                                                  Ага, конечно, видимо именно потому что в C++ нет обратной совместимости с C, вы можете использовать в нем одновременно: массивы C ([]) и массивы C++ (std::vector), строки C (char*) и строки C++ (std::string), вывод C (printf) и вывод C++ (std::iostream), выделение памяти C (malloc/free) и выделение памяти C++ (new/delete), указатели C (*) и ссылки C++ (&), и т.д, и т.п.
                                                                                                                    +1
                                                                                                                    Пара ремарок.
                                                                                                                    1) Массивы C++ — std::array.
                                                                                                                    2) Желаю удачи с неявными кастами из\в void*.
                                                                                                                      –1
                                                                                                                      О, ну еще лучше — значит вместо двух типов массивов их теперь в C++ целых три. И на Stackoverflow наверняка есть куча вопросов про то, чем std::vector отличается от std::array, а std::array от T[]. Конечно, не только в C++ можно делать одно и то же разными способами, но дизайн языка от этого лучше не становится.
                                                                                                                        +2
                                                                                                                        Невежество не повод для гордости. std::vector — не массив, а очередь с произвольным доступом. Так что типа ровно два, и один из них унаследован от C.
                                                                                                                          0
                                                                                                                          Требование о выделении памяти под данные цельным куском — вполне себе признак массива.
                                                                                                                          Проще различать их как vector — данные в куче, array — данные на стеке. Соответственно размер array изменить нельзя.
                                                                                                                            –2
                                                                                                                            Содержимое vector может перемещаться в пространство-времени, то есть любое изменение размеров vector'а может привести указатели на произвольные элементы вектора в негодное состояние. Согласитесь, это не стандартное поведение массивов по эту сторону планеты.
                                                                                                                              –1
                                                                                                                              любое изменение размеров vector'а может привести указатели на произвольные элементы вектора в негодное состояние

                                                                                                                              Вовсе не любое, а только такое, после которого «new size is greater than the old capacity».
                                                                                                                                +2
                                                                                                                                И как, согласно этому описанию, vector перестаёт быть массивом?
                                                                                                                                realloc() тоже делает все указатели на элементы в памяти невалидными, но он таки выделяет память под массив, динамический, в куче… это я точно не про vector сейчас?
                                                                                                                                  0
                                                                                                                                  Давайте быть корректными до упора, malloc, realloc и free работают с блоками памяти, они совершенно не интересуются, массив это или нет. Не забывайте, *alloc принимают не число элементов, а размер в байтах. realloc завершается успешно, даже если из ста элементов удалось переместить 3,141592. π скопированных элементов — это нормальное поведение массивов?
                                                                                                                                    –1
                                                                                                                                    А почему забыли calloc?
                                                                                                                                    они совершенно не интересуются, массив это или нет.

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

                                                                                                                                    Типы — это свойство интерфейса. На этом уровне — указателя.

                                                                                                                                    блоками памяти

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

                                                                                                                                    Не забывайте, *alloc принимают не число элементов, а размер в байтах

                                                                                                                                    А что такое байт? Это не элемент?

                                                                                                                                    realloc завершается успешно, даже если из ста элементов удалось переместить 3,141592. π скопированных элементов — это нормальное поведение массивов?

                                                                                                                                    Чё? Если вектор на аппенде зафейлится, то чем его поведение будет отличатся от реалока?

                                                                                                                                      +1
                                                                                                                                      А что такое байт? Это не элемент?

                                                                                                                                      На адекватном уровне типизации — нет.

                                                                                                                                      Рассматривать любой объект (так, как он определяется в стандарте) как массив байт и исходя из этого определять понятие массива можно, конечно, но представляется неконструктивным.
                                                                                                                                        –4
                                                                                                                                        Очередное игнорирование всего и вся.

                                                                                                                                        На адекватном уровне типизации — нет.

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

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

                                                                                                                                        Опять какая-то брехня. Какой ещё объект? В памяти нет никаких объектов. Память это и есть массив адресуемых елементов.

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

                                                                                                                                        Т.е. если мы имеем какой-то адрес и его длину( т.е. блоками памяти по вашему), то на самом деле мы имеем массив из всех значений от значения адреса текущего до значения адрес + длинна.

                                                                                                                                        Всё просто.

                                                                                                                                        И без разницы как это дальше интерпретировать — память это массив. По определению.

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

                                                                                                                                          На уровне памяти и массивов не существует. На уровне памяти вся память — один большой единственный массив.

                                                                                                                                          Какой ещё объект? В памяти нет никаких объектов.

                                                                                                                                          Объект — это, если сильно упрощать, вот эта самая адресуемая штука. int a; struct {} foo;, и так далее.

                                                                                                                                          Память это и есть массив адресуемых елементов.

                                                                                                                                          Вы не можете так относиться в памяти к C++.

                                                                                                                                          Т.е. если мы имеем какой-то адрес и его длину( т.е. блоками памяти по вашему), то на самом деле мы имеем массив из всех значений от значения адреса текущего до значения адрес + длинна.

                                                                                                                                          А объекты вы через memcpy копируете? Даже с виртуальными функциями и всем таким? Норм вам тама.

                                                                                                                                          Только не сливайтесь, пожалуйста, что рассуждаете про совместимое с С подмножество С++, это бессмысленно.
                                                                                                                                            –3
                                                                                                                                            На уровне памяти и массивов не существует. На уровне памяти вся память — один большой единственный массив.

                                                                                                                                            Ой балаболка. Дак память массив или не массив? Ну ладно тут лужа.

                                                                                                                                            А теперь подумаем, о чём же мы говорили? Ой мы говорили о:

                                                                                                                                            блоками памяти


                                                                                                                                            Т.е. кусок массива( блок есть кусок) не является массивом? Вот это новость.

                                                                                                                                            Объект — это, если сильно упрощать, вот эта самая адресуемая штука. int a; struct {} foo;, и так далее.

                                                                                                                                            Не верно. Объекты существует на уровне языка — это интерпретация памяти. На уровне памяти же никаких объектов не существует.

                                                                                                                                            Собственно на этой логике построены юнионы. Ну куда там балаболам.

                                                                                                                                            Вы не можете так относиться в памяти к C++.

                                                                                                                                            Опять какие-то пустые утверждения. На основании чего вдруг я не могу? Поподробнее.

                                                                                                                                            А объекты вы через memcpy копируете? Даже с виртуальными функциями и всем таким? Норм вам тама.

                                                                                                                                            Какие такие объекты? Причём тут объекты. Говорилось про «блок памяти» — блок памяти есть массив. С этим вы выше согласились. Зачем эти ваши потуги? Какое они имеют отношения к теме?

                                                                                                                                            Да, объекты копируются мемкопи — любые. И объясняется этот факт очень просто. Вы там выше балаболили про new, дак вот как работает new? Правильно через sizeof.

                                                                                                                                            Т.е. при создании объекта он создаётся в «блоке памяти», который явно определён. И этот блок ничего не мешает копировать.

                                                                                                                                            Тут вы пытаетесь слиться на тот факт, что виртуальные(как и любые) куллстори это могут быть не только данные, но и какая-то левая логика. Но потуги не имеют смысла, ибо левая логика не есть данные и не есть память.

                                                                                                                                            При это какое отношения имеет ваш высер к той цитате на которую вы отвечаете — вы не ответите, ибо он отношения не имеет.

                                                                                                                                            Заметим. Высер аналогичный той тактике, что я описал в каком-то комменте. Мы высираем какую-то не относящуюся к делу херню и на основании «потому что я так сказал» делаем вывод, что «оппонент дурак».

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

                                                                                                                                            Балаболка пытается меня ловить на том, что я якобы куда-то сливают. Можно мне доказательства?
                                                                                                                                              +3
                                                                                                                                              Дак память массив или не массив?

                                                                                                                                              На уровне процессора и ассемблера — массив байт. На уровне языка — нет.

                                                                                                                                              Т.е. кусок массива( блок есть кусок) не является массивом?

                                                                                                                                              Вы путаете уровни. Постарайтесь распутаться.

                                                                                                                                              На уровне памяти же никаких объектов не существует.

                                                                                                                                              А мы говорим про уровень языка. Что нам этот уровень памяти, если мы о различиях в языках говорим?

                                                                                                                                              Собственно на этой логике построены юнионы. Ну куда там балаболам.

                                                                                                                                              Да, и эту логику юнионы инкапсулируют (и, кстати, поговаривают, писать в один член юниона, а читать из другого — UB). Но инкапсуляция — для балаболов, чоткие пацаны смешивают понятия, ясно.

                                                                                                                                              Опять какие-то пустые утверждения. На основании чего вдруг я не могу?

                                                                                                                                              Потому что копировать объекты через memcpy вы не можете, например.

                                                                                                                                              Говорилось про «блок памяти» — блок памяти есть массив. С этим вы выше согласились.

                                                                                                                                              У вас быстро теряется контекст. Выше мы говорили о блоке памяти с точки зрения процессора. Язык это дело внезапно прячем.

                                                                                                                                              Да, объекты копируются мемкопи — любые.

                                                                                                                                              Вон из профессии.

                                                                                                                                              И этот блок ничего не мешает копировать.

                                                                                                                                              Мешает семантика языка и наличие конструкторов копирования, которые надо вызывать. Копировать вы можете только POD-типы (в C++03, в C++11 чуть сложнее).

                                                                                                                                              Балаболка пытается меня ловить на том, что я якобы куда-то сливают. Можно мне доказательства?

                                                                                                                                              Этот тред тому доказательство, юное создание.
                                                                                                                                                –2
                                                                                                                                                На уровне процессора и ассемблера — массив байт. На уровне языка — нет.

                                                                                                                                                На уровне языка так же. В целом споры с балаболами лсными бесмысленны — зачем я этим страдаю?

                                                                                                                                                На уровне языка память никакой типизацией не обладает. Собственно поэтому аллокаторы и возвращают void *.

                                                                                                                                                Типизация памяти является свободной — это определено на уровне языка. У памяти есть только свойство «длинна» и начальный адрес. По определению понятие адреса и длинные( опять же в языке длинна является ничем иным как офсетом между началом и не включенным конечным значением) представляют множество всех значений. По умолчанию используемой единицей адресации на уровне языка определён char, а на уровне модели памяти любое значение числа в промежутке «между» допустимыми.

                                                                                                                                                Да, и эту логику юнионы инкапсулируют (и, кстати, поговаривают, писать в один член юниона, а читать из другого — UB). Но инкапсуляция — для балаболов, чоткие пацаны смешивают понятия, ясно.

                                                                                                                                                О боже, опять какой-то убогий высер.

                                                                                                                                                Чтение и записать в любые члены юниона не является уб. Ты получишь ровно то, что там есть. Это валидная операция.

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

                                                                                                                                                Потому что копировать объекты через memcpy вы не можете, например.

                                                                                                                                                Какие такие объекты? Мемкопи копирует память. Копировать представление в памяти любого объекта я могу.

                                                                                                                                                В целом потуги ясно. Балаболка обосралась и пытается съехать с темы памяти и объектов в памяти на тему объектов в логики смеси данных и исполняемой логики.

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

                                                                                                                                                Вон из профессии.

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

                                                                                                                                                Мешает семантика языка и наличие конструкторов копирования, которые надо вызывать. Копировать вы можете только POD-типы (в C++03, в C++11 чуть сложнее).

                                                                                                                                                В целом слив пошел по тому пути, что я заранее определил.

                                                                                                                                                Какое отношение семантика языка имеет к памяти?

                                                                                                                                                Этот тред тому доказательство, юное создание.

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

                                                                                                                                                Ничего удивительного в этом нет. Из таких убогих состоит людей помойка.
                                                                                                                                                  +3
                                                                                                                                                  На уровне языка память никакой типизацией не обладает. Собственно поэтому аллокаторы и возвращают void *.

                                                                                                                                                  Только когда мне нужен объект, я не дёргаю аллокатор, а потом по нему фигачу placement new, а сразу вызываю new. А new мне даёт совсем не void*.

                                                                                                                                                  Типизация памяти является свободной — это определено на уровне языка.

                                                                                                                                                  Это что-то новое. Что именно определено на уровне языка? Что я в любой байт памяти могу написать любое значение? Или что означает, что типизация памяти свободная?

                                                                                                                                                  Чтение и записать в любые члены юниона не является уб. Ты получишь ровно то, что там есть. Это валидная операция.

                                                                                                                                                  Не в плюсах. Постарайтесь осилить прочитать.

                                                                                                                                                  Кстати, лишняя демонстрация различия языков на уровне идеологии.

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

                                                                                                                                                  У UB нет уровней (как вы себе это представляете, в конце концов?). Оно либо есть, либо его нет.

                                                                                                                                                  К возможности её чтения то же.

                                                                                                                                                  Очевидно, что имеет прямое же.

                                                                                                                                                  Копировать представление в памяти любого объекта я могу.

                                                                                                                                                  В переменную такого же типа — нет. В массив чаров, разве что, возможно, можете, но это мне надо уточнить.

                                                                                                                                                  Балаболка обосралась

                                                                                                                                                  Вы к себе слишком жестоки.

                                                                                                                                                  и пытается съехать с темы памяти

                                                                                                                                                  А что память обсуждать? Мы язык обсуждаем и обеспечиваемые языком средства работы с памятью.

                                                                                                                                                  Только штука в том, что исполняемая логика к мамяти отношения не имеет.

                                                                                                                                                  Ну и к чему вы изначально начали говорить про массив с точки зрения памяти?

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

                                                                                                                                                  Скопируйте один std::string в другой через memcpy, а я на это посмотрю.

                                                                                                                                                  Да и зачем мне из себя что-то строить, я про себя-то уже всё знаю.

                                                                                                                                                  Какое отношение семантика языка имеет к памяти?

                                                                                                                                                  Очевидное и прямое: с памятью вы можете работать только средствами языка.

                                                                                                                                                  Из таких убогих состоит людей помойка.

                                                                                                                                                  Ваша воинственная глупость делает меня печальным.
                                                                                                                                        +1
                                                                                                                                        > Естественно, ибо ни железяка, ни рантайм, ни что-либо ещё не знает о существовании каких-то массивов о которых говорит эксперт с хабра.
                                                                                                                                        Напомню, что у функций Си в параметрах элементарные типы, либо структуры, либо указатели на них. Вызвать printf с первым параметром типа FILE получится после изнасилования компилятора. Да, си, а вслед за ним и С++ позволяет насиловать как компилятор, так и железо непосредственно, но нисколько не поощряет такое поведение.

                                                                                                                                        > Удивлю, но память и есть массив адресуемых элементов.
                                                                                                                                        На уровне железа? На уровне железа, раз уж вас так сильно тянет вниз, у нас есть блоки SDRAM или других типов, которые не обязательно имеют один размер, не обязательно имеют сплошную или одномерную адресацию и не обязательно являются непрерывными. На уровне ОС? Блоки (да что-же за школьное слово-то!) виртуальных адресов, проецируемых на те самые физические адреса. Уже на уровне ОС память не является одномерным массивом, так как несколько приложений способны иметь одинаковую локальную виртуальную адресацию, которую ОС распихивает и разгребает.

                                                                                                                                        > Опять же на уровне языка этого нет.
                                                                                                                                        Ну давай, завкаф филфака, удиви, что же есть на уровне языка?

                                                                                                                                        > А что такое байт? Это не элемент?
                                                                                                                                        Нет, если у меня массив int.

                                                                                                                                        > Чё? Если вектор на аппенде зафейлится, то чем его поведение будет отличатся от реалока?
                                                                                                                                        Выбрасыванием исключения std::bad_alloc? Не знаю… Чем же такое поведение отличается от ошибки выделения памяти в том же Питоне?
                                                                                                                                          –2
                                                                                                                                          Напомню, что у функций Си в параметрах элементарные типы, либо структуры, либо указатели на них.

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

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

                                                                                                                                          Вызвать printf с первым параметром типа FILE получится после изнасилования компилятора.

                                                                                                                                          Чего? Поподробней про насилие при передачи указателя.

                                                                                                                                          На уровне железа?

                                                                                                                                          На уровне того, что там указано. А раз иного не указано, то уровень того о чём собственно говориться — т.е. си, да и в целом программном уровне. Ибо любая программа работает на этом уровне и ничего снизу к ней отношения не имеет.

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

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

                                                                                                                                          Из этого ничего не следует. Зачем мне об этом рассказывать? От этого этого определение памяти не меняется.

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

                                                                                                                                          Это верно и для уровня ОС и для уровня приложения. «блоки» на уровне ОС есть ни что иное как объект уровня менеджмента, а не памяти. В любом случае всё это в конечном итоге эмулирует то определение памяти, что я дал.

                                                                                                                                          Точно так же и процессор эмулирует для ОС определение памяти, что я дал. А далее это уже уровень не программный.

                                                                                                                                          Ну давай, завкаф филфака, удиви, что же есть на уровне языка?

                                                                                                                                          Зачем мне задан этот вопрос? Из чего он следует?

                                                                                                                                          Может не ясно почему логика рантайма и логика программиста не являются уровнем языка?

                                                                                                                                          Нет, если у меня массив int.

                                                                                                                                          Опять 25. Что за тотальный аутизм. Массив, а в данном случае указатель — есть интерфейс, а не память. Нет никаких различий между int и не int на уровне памяти. По определению и языка, и матчасти и логики.

                                                                                                                                          Выбрасыванием исключения std::bad_alloc?

                                                                                                                                          Опять какая-то ахинея вместо ответа. Вы написали херню:

                                                                                                                                          realloc завершается успешно, даже если из ста элементов удалось переместить 3,141592. π скопированных элементов — это нормальное поведение массивов?

                                                                                                                                          Мало того, что это какая-то херня, да и к тому же вы написали «это нормальное поведение для массивов?» — т.е. намекая на ненормальное поведение.

                                                                                                                                          Вас спросили чем это поведение отличается от «массива» вашего. Вы ответили «не знаю». О какой вообще вашей вменяемости может идти речь? Если вы не знаете — нахрен вы это пишите?

                                                                                                                                            +1
                                                                                                                                            Так работает присваивание в си, что присваивание некопируемоего объекта равняется присваиванию указателя на оный.

                                                                                                                                            Да ну?


                                                                                                                                            int a[10], b[10];
                                                                                                                                            a = b; // error: assignment to expression with array type

                                                                                                                                            Капитан Очевидность сообщает: некопируемый объект — он реально некопируемый.

                                                                                                                                              –2
                                                                                                                                              > Так работает присваивание в си
                                                                                                                                              Вы не поняли. Я знаю, что везде передаются указатели. Но на уровне языка передаются указатели на тип. Не абстрактный адрес, а вполне конкретные свойства объекта. Если типы структур или сигнатуры методов не совпадают, компилятор выдаёт ошибку. Компилятор. Вы можете подавить его, заставить быть тупой железякой. Но из коробки он весьма умный и въедливый парень.

                                                                                                                                              > Чего? Поподробней про насилие при передачи указателя.
                                                                                                                                              http://coliru.stacked-crooked.com/a/f200471b2d6ca492

                                                                                                                                              > Зачем мне задан этот вопрос?
                                                                                                                                              >> массив адресуемых элементов. Опять же на уровне языка этого нет.
                                                                                                                                              Я просто поинтересовался, как же так, на уровне языка нет массивов, если есть operator[]?

                                                                                                                                              > По определению и языка
                                                                                                                                              http://coliru.stacked-crooked.com/a/45b48688dc07146a
                                                                                                                                              Не сходятся концы.

                                                                                                                                              > ахинея
                                                                                                                                              Мне очень жаль, что машинный текст не способен передавать интонации голоса, как способен это делать рукописный. Последние два предложения следовало читать с саркастически-издевательской интонацией.
                                                                                                                                              Я действительно не знаю, как питон даст знать об окончании памяти, но я точно знаю, что после неуспеха выделения памяти вектор отменит эту операцию, не потеряв ни одного из уже имеющихся элементов. И тем более, не будет мне петь «Всё хорошо, прекрасная маркиза...», скопировав мне три с половиной землекопа. Для меня очевидно, что на уровне языка корректно только такое поведение.
                                                                                                                                              realloc же его нарушает, что даже с точки зрения языка Си не очень корректно, и именно это заставляет си-программистов каждый раз бить его по голове операторами явного приведения адреса памяти к указателю на тип. Хорошо, что у плюсовиков такой проблемы нет…
                                                                                                                                  0

                                                                                                                                  Структуры, аналогичные std::vector по свойствам, часто называют динамическими массивами.