Ваша мова програмування — відстій

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-синтаксис
"<<<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", "с");
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, він має надлишкову ключове слово «якщо» («поки не»). Можна виконати операцію «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 <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{};

• Є спроба, а не остаточно

• Розглянута «особливість», тому що 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©)
, що виглядає досить незграбно.

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

• Масиви не працюють з узагальненими типами: неможливо створити масив з типом змінної
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
веде до типів hairy всюди.

Виправлено в 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-color 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}|{cargo}
• nmchar [a-z0-9-]|{nonascii}|{cargo}

• У розділі «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
надто складна.

• Відсутні загальні функціональні структури на зразок Монада і 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" є каналом, то "і" є елементом, зчитуваним з каналу
// якщо "d" є картою, то "і" є умовним позначенням
// інакше "і" є індексом масиву/сукупності/рядка
}

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

• Go дозволяє дати імена її обчислене значення, завдяки чому можна неявним чином повернути їх. Він також дозволяє перевизначити змінні у внутрішніх областях, перекриваючи визначення в зовнішніх областях. Це може сприяти нечитаемому кодом саме по собі, але взаємодія між цими двома мовними особливостями просто дивовижно:

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

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

func foo3(int i) (r int) {
if i != 0 {
r := 12
fmt.Println®
return // ПОМИЛКА: "r" перекрито при поверненні
}
return
}

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

func dogma() (int i) {
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, то він ніколи не був би дозволений до використання Федеральним авіаційним агентством.

• Повідомлення про помилки від глибоко вкладених макросів? Це саме те, що ви очікуєте.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.