[Перевод] Ваш язык программирования отстой

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 Обсуждение
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-синтаксис "<< не может быть использован в инициации значений по умолчанию атрибутов метода на 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}");

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

• Код всегда должен находиться между тегами и ?>, даже если это не 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, где вместо изучения работы логического потока, вы просто вводите отступ, чтобы показать связь.

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

• Однако даже при этом бывают случаи, когда 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 », что заставляет использовать модели вроде »while not

• Синтаксис для условного выражения в 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? Кто называет это

© Habrahabr.ru