Яка найбільша вада дизайну, з якою ви стикалися в будь-якій мові програмування? [зачинено]


29

Усі мови програмування мають свої недоліки в дизайні просто тому, що жодна мова не може бути ідеальною, як і більшість (усіх?) Інших речей. Що убік, яка помилка дизайну в мові програмування вас найбільше дратувала через вашу історію як програміста?

Зауважте, що якщо мова "погана" лише тому, що вона не розроблена для конкретної речі, це не вада дизайну, а особливість дизайну, тому не перераховуйте таких неприємностей у мовах. Якщо мова не відповідає тому, для чого вона призначена, це, звичайно, недолік у дизайні. Реалізація конкретних речей і під капотом речі теж не враховуються.


6
Зауважте, що це конструктивно для мовних дизайнерів (помилок, яких слід уникати), якщо хтось хотів би поставити під сумнів, наскільки це питання конструктивне.
Анто-

Очевидний
обман

1
@greyfade: Насправді мова йде про недоліки у власній мові. Це, здається, стосується речей, які знижують прийняття мови, які можуть включати погану стандартну бібліотеку або просто поганий веб-сайт для цієї мови. Деякі списки відповідей, наприклад, поганий синтаксис, але це не конкретна вада дизайну
Anto

8
Найбільший недолік у будь-якій мові програмування? Люди.
Джоель Етертон

Якщо ці відповіді є найбільшими вадами, то мене дуже вражають мови програмування.
Том Хотін - тайклін

Відповіді:


42

Одне з моїх великих неприємностей - це те, як switchвипадки у мовах, що походять зі С, за замовчуванням переходять до наступного випадку, якщо ви забудете використовувати break. Я розумію, що це корисно в коді дуже низького рівня (наприклад , пристрій Даффа ), але він зазвичай не підходить для коду рівня програми та є загальним джерелом помилок кодування.

Я пам’ятаю, приблизно в 1995 році, коли я вперше читав про подробиці Java, коли я потрапив до частини про switchтвердження, я був дуже розчарований, що вони зберегли поведінку за промовчанням за замовчуванням. Це просто перетворюється switchна прославлене gotoіншим ім'ям.


3
@Christopher Mahan: switchне треба так працювати. Наприклад, випадок Ада / коли заява (еквівалентна комутатору / випадку) не має поведінки пропускання.
Грег Хьюгілл

2
@Greg: Висловлювання, switchподібні мовам, не пов'язаним із C, не повинні працювати таким чином. Але якщо ви використовуєте потік управління C-стилі ( {... }, for (i = 0; i < N; ++i), returnі т.д.), мова умовивід змусить людей очікувати , switchщоб працювати , як C, і даючи йому Ada / Pascal / BASIC-як семантика б сплутати людей. C # вимагає breakу switchвисловлюваннях з тієї ж причини, хоча робить її менш схильною до помилок, забороняючи беззвучне проривання. (Але я б хотів, щоб ви могли написати fall;замість негарного goto case.)
dan04

4
тоді не пишіть перемикач, пишіть if () else. причина, на яку ти скаржишся, полягає в тому, що робить перемикання кращим: це не справжній / хибний умовний, це цифрова умовна, і це робить його різним. Ви також можете написати свою функцію комутації.
jokoon

9
-1 Я вважаю користю дозволити пробитися, немає нічого небезпечного, крім глупоти, але він надає додаткову функцію.
Орблінг

11
Проблема не в тому, що switch дозволяє провалитися. Це те, що більшість застосувань для падіння не є навмисними.
dan04

41

Мені ніколи не сподобалось використання =для призначення та ==для тестування рівності на мовах, що походять з С. Потенціал плутанини та помилок занадто високий. І навіть не запускайте мене ===в Javascript.

Краще було б :=для призначення та =для перевірки рівності. Семантика могла бути точно такою, якою є сьогодні, коли присвоєння - це вираз, який також дає значення.


3
@Nemanja Trifunovic: Я спочатку думав про цю пропозицію, але вона має сумну неоднозначність у С із меншим порівнянням порівняно з негативним числом (тобто x<-5). Програмісти на С не терплять такого необхідного пробілу :)
Грег Хьюгілл

7
@Greg: Я вважаю за краще :=і ==тому, що було б занадто просто забути :і не повідомити, як це вже є (хоча і повернено), коли ви забудете =сьогодні. Я вдячний за попередження укладача щодо цього ...
Матьє М.

13
Практично на всіх клавіатурах, які я коли-небудь використовував, ": =" вимагає зміни клавіші shift під час введення її. На тому, що я зараз використовую, ':' є великими літерами, а '=' є малі, і я змінив це. Я набираю багато завдань, і мені не потрібно такого типу введення клопоту в них.
Девід Торнлі

14
@David Thornley: Код читається набагато більше разів, ніж написано. Я не купую жодних аргументів щодо "набору клопоту".
Грег Хьюгілл

8
@Greg Hewgill: Впевнений, що його читають частіше, ніж написано. Однак проблема між =і ==не читання, тому що вони різні символи. Це в письмовій формі та переконайтесь, що ви отримали потрібний.
Девід Торнлі

29

Страшна помилка вибору +в Javascript як для додавання, так і для з'єднання рядків . Оскільки значення не типізовані, це призводить до візантійських правил, які визначають +, додаватимуть чи об'єднуватимуться залежно від точного вмісту кожного операнда.

На початку було б легко запровадити абсолютно новий оператор, такий як $конкатенація рядків.


13
@Barry: Не дуже. +має багато сенсу як оператор конкатенації рядків у сильно набраній мові. Проблема полягає в тому, що Javascript використовує його, але сильно не набраний.
Мейсон Уілер

13
@ Мейсон: +не має сенсу для конкатенації, оскільки конкатенація, безумовно, не є комутативною. Що стосується мене, це зловживання.
Матьє М.

12
@Matthieu: Хм ... чому це важливо? Сполучення не є комутативним (як додаток), але додавання двох рядків є безглуздим, тому ніхто не думає про це таким чином. Ви вигадуєте проблему там, де її немає.
Мейсон Уілер

4
просто перейдіть на C ++ і додайте будь-якого виду до езотеричної перевантаження оператору на ваш вибір
ньютопієць,

8
@Matthieu: Комутативність тут не проблема. Якби JS мав клас Matrix, чи вважаєте ви це зловживанням для нього мати *оператора?
dan04

24

Я вважаю Javascript за замовчуванням глобальним для основної проблеми, і часто є джерелом помилок, якщо ви не використовуєте JSLint тощо


2
Типовий для глобального? Я радий, що тоді не використовую JS ...
Анто,

5
Насправді це дуже гарна мова, в ній є кілька поганих варіантів дизайну. Це головний, якщо ви не застосуєте змінну, вона охопить її як глобальну. Хороша річ, що за допомогою програми Jslint Дуга Крокфорда ви можете зрозуміти цю помилку і ще багато.
Захарій К

1
Просто використовуйте varкожен раз, коли ви оголошуєте змінну, і ви хочете перейти. І не кажіть мені, що це занадто багато набирати текст, тому що Java змушує вас оголошувати всі типи двічі, і ніхто не скаржиться на те, що це вибір неприємного дизайну.
davidk01

3
@ davidk01: Люди на це скаржаться. Так само люди скаржилися на необхідність декларувати std::map<KEY, VALUE>::const_iteratorзмінні в C ++ достатньо, autoщо додається до цієї мови.
dan04

1
"use strict"директива додана в ES5, змінює не задекларовані посилання на помилку.
Шон Макміллан

22

Препроцесор в C і C ++ - це масова хитрість, створює абстракції, які просочуються, як сита, заохочує код спагетті через гніздо #ifdefвисловлень щурів і вимагає жахливо нечитаних ALL_CAPSімен, щоб вирішити його обмеження. Корінь цих проблем полягає в тому, що вона діє на текстовому рівні, а не на синтаксичному чи семантичному рівні. Її слід було замінити справжньою мовою для різних її випадків використання. Ось кілька прикладів, хоча, правда, деякі з них вирішені в C ++, C99 або неофіційних, але фактично стандартних розширеннях:

  • #include повинні були бути замінені реальною модульною системою.

  • Вбудовані функції та шаблони / дженерики можуть замінити більшість випадків використання функціональних викликів.

  • Якась функція константи часу маніфеста / компіляції може бути використана для оголошення таких констант. D-розширення enum тут чудово працюють.

  • Реальні макроси синтаксичного дерева можуть вирішити безліч різних випадків використання.

  • Струнні міксини можуть бути використані для випадку використання коду.

  • static ifабо versionтвердження можуть використовуватися для умовної компіляції.


2
@dsimcha: Я згоден з #includeпроблемою, але модульна система була винайдена ... згодом! І C, і C ++ прагнуть досягти максимальної відсталої сумісності: /
Матьє М.

@Matthieu: Так, модульні системи були винайдені післязагальні слова, але ми як би там не говорили про задній огляд.
dimimcha

3
Я погоджуюся, що це великий біль у різних частинах кузова, але багатопрохідний дизайн має перевагу у простоті: компілятору C не потрібно багато знати про контекст роботи, перш ніж він зможе скласти фрагмент коду С . Це можна кваліфікувати лише як помилку дизайну, якщо ви можете показати, що витрати на використання гіпотетичної модульної системи в самій мові C (наприклад, C ++ - подібні класи) завжди нижчі або порівнянні з існуючими #includeхакерськими процесами на основі CPP .
reinierpost

Я згоден. Однак деякі вважають, що попередня обробка - це добре, і скаржаться, що вона не підтримується (наприклад) на Java.
Стівен C

5
@Stephen: Я погоджуюся, що Java з препроцесором може бути кращим, ніж Java без, але тільки тому, що Java не має декількох "реальних" функцій, необхідних для заміни препроцесора. У таких мовах, як D, які включають такі функції, і Python, який отримує гнучкість іншими способами, будучи динамічним, я не пропускаю цього ні одного біта.
dimimcha

21

Можна було б перерахувати сотні помилок на сотнях мови, але ІМО - це не корисна вправа з точки зору дизайну мови.

Чому?

Тому що щось, що було б помилкою в одній мові, не було б помилкою в іншій мові. Наприклад:

  • Перетворення C на керовану (тобто зібрану сміття) мову або приборкання примітивних типів обмежило б її корисність як напівпереносну мову низького рівня.
  • Додавання Java-пам’яті управління стилем C (наприклад, для вирішення проблем щодо продуктивності) може порушити це.

Там є уроки , які можна витягнути, але уроки рідко зрозумілі, і щоб зрозуміти їх , ви повинні розуміти технічні компроміси ... і історичний контекст. (Наприклад, громіздка реалізація дженериків на Java - це наслідок важливої ​​вимоги бізнесу щодо підтримки зворотної сумісності.)

ІМО, якщо ви серйозно ставитесь до проектування нової мови, вам потрібно фактично використовувати широкий спектр існуючих мов (і вивчати історичні мови) ... і скласти власну думку про те, які помилки. І потрібно мати на увазі, що кожна з цих мов була розроблена в певному історичному контексті, щоб заповнити певну потребу.

Якщо є загальні уроки, які слід засвоїти, вони знаходяться на "мета" рівні:

  • Ви не можете створити мову програмування, яка ідеально підходить для всіх цілей.
  • Ви не можете уникнути помилок ... особливо якщо дивитися ззаду.
  • Багато помилок боляче виправити ... для користувачів вашої мови.
  • Ви повинні враховувати передумови та навички цільової аудиторії; тобто існуючі програмісти.
  • Ви не можете догодити всім.

9
-1 - мови програмування відповідають цілям проектування. Особливості мови, яка працює проти цих цілей, є вадами дизайну, якщо це не є необхідним компромісом для однієї з інших її цілей. Не так багато мов створено з метою задоволення всіх, але всі мови повинні намагатися в першу чергу задовольнити людей. Цей вид постмодерністської політичної коректності досить задушливий для вивчення та розробки мови програмування.
Рей Міясака

Моя думка полягає в тому, що важко засвоїти уроки без урахування цілей дизайну.
Stephen C

1
мені здається, що ОП вже врахувала вашу відповідь у другому пункті питання.
Айдан Каллі

20

C і C ++ : усі цілі типи, які нічого не означають .

Особливо char. Це текст чи це крихітне ціле число? Якщо це текст, це символ "ANSI" або блок коду UTF-8? Якщо це ціле число, чи воно підписане чи без підпису?

int повинен був бути "нативним" цілим числом, але для 64-бітних систем це не так.

longможе бути або не бути більшим за int. Він може бути або не бути розміром вказівника. Це майже довільне рішення з боку авторів-компіляторів, будь то 32-бітне чи 64-бітове.

Безумовно, мова 1970-х. Перед Unicode. До 64-бітних комп'ютерів.


10
C не був розроблений як стандартна мова для всіх, тому це не може бути помилкою в дизайні. Він був розроблений для моделювання процесора як портативного асемблера, уникаючи специфічного для процесорного коду асемблера. Однак це було виправлено на Java.

7
Я думаю, що більша проблема - це новіші мови, які продовжують використовувати ті самі безглузді терміни, незважаючи на те, що історія показує нам, що це жахлива ідея. Принаймні хлопці C помітили свою помилку і створили стандартні типи int.
Марк Н

5
Значок не є одиницею utf-8. Символ utf-8 може зберігати більше, ніж 8 біт. C - це не мова 1970-х, я зараз її використовую для проекту (добровільно).
dan_waterworth

4
C - трохи більше, ніж абстракція процесора PDP-11 високого рівня. Наприклад, PDP-11 безпосередньо підтримували ПДП-11 та після нього.
біт-твідлер

5
Це жахливо помилкова відповідь. По-перше, C і C ++ не є взаємозамінними. По-друге, мовна специфікація чітко визначає, що таке символ - Об'єкт, оголошений типом char, досить великий, щоб зберігати будь-якого члена основного набору символів виконання. . По-третє, C - це не "мова 70-х", це мова, яка живе близько до обладнання та, ймовірно, мова, яка в кінцевому підсумку дозволяє всі ваші абстракції високого рівня насправді мати сенс для процесора. Ви приїжджаєте як людина, яка знає лише мови високого рівня і не має розуміння того, як справи насправді працюють. -1
Ед С.

18

null.

Його винахідник Тоні Хоар називає це "помилкою в мільярд доларів" .

Він був введений в ALGOL в 60-х роках і існує в більшості часто використовуваних мов програмування сьогодні.

Краща альтернатива, що використовується в таких мовах, як OCaml та Haskell, - можливо . Загальна думка полягає в тому, що посилання на об'єкти не можуть бути нульовими / порожніми / неіснуючими, якщо тільки немає явних вказівок, що вони можуть бути такими.

(Хоча Тоні дивовижний у своїй скромності, я думаю, що майже хто-небудь допустив би таку ж помилку, і він просто трапився першим.)


Не згоден навіть із його винахідником !!! null - порожнє значення для типу даних вказівника / посилання. У рядках порожній рядок, в наборах є порожній набір (Паскаль порожній набір = []), цілі числа - 0. Більшість мов програмування, які використовують null / nil / будь-що, якщо змінна присвоєна належним чином, помилку нуля можна запобігти.
umlcat

4
@ user14579: У кожній мові, яка підтримує будь-який набір або рядок або масив, є {}, але такий спосіб все ще є семантично доречним і не вийде з ладу, якщо у вас вже є щось, що могло б викликати помилку межі масиву - але це інша проблема. Ви можете обробити порожній рядок з великого регістру всіх символів, що призведе до порожнього рядка. Ви спробуєте те ж саме на нульовому рядку, і без належного розгляду воно вийде з ладу. Проблема полягає в тому, що цей належний розгляд є виснажливим, часто забувається і ускладнює написання одновиражних функцій (тобто лямбда).
Рей Міясака

1
Я заробляю гроші щоразу, коли я набираю null ... о так, хтось втрачає гроші щоразу, коли я набираю null. За винятком цього разу.
kevpie

3
@umlcat - коли у вас є мови з відповідністю шаблонів, такі як Ocaml, Haskell і F #, використовуючи можливо x | Жоден шаблон не заважає забути нульовий регістр під час компіляції. Жодна кількість хитрощів під час компіляції не може ввести помилку в мовах, де null є встановленою ідіомою. Оскільки ви повинні чітко вирішити не мати справу з нульовою справою мовами, які мають монаду "Можливо" та "Деякі", вони мають серйозну перевагу перед "нульовим" підходом.
JasonTrue

1
@Jason - Мені подобається вважати maybeнульовою відмову, тоді як нульовим винятком є ​​відмова. Зрозуміло, що слід сказати і про різницю між помилками виконання та помилками під час компіляції, але саме той факт, що null - це, по суті, ін'єкційна поведінка.
Рей Міясака

14

У мене виникає відчуття, що люди, які розробили PHP, не використовували звичайну клавіатуру, вони навіть не використовують клавіатуру colemak, тому що вони повинні зрозуміти, що вони роблять.

Я розробник PHP. PHP не цікаво вводити.

Who::in::their::right::mind::would::do::this()? ::Оператор вимагає зсуву утримання , а потім два натискання клавіш. Яка трата енергії.

Хоча-> це-> є-> не-> набагато-> краще. Для цього також потрібні три натискання клавіш, зсув яких знаходиться між двома символами.

$last = $we.$have.$the.$dumb.'$'.$character. Знак долара використовується надзвичайно багато разів і вимагає розтягування нагород до самої вершини клавіатури плюс натискання клавіші shift.

Чому вони не могли розробити PHP для використання клавіш, які набагато швидше набирати? Чому не вдалося we.do.this()або почати функцію vars з ключа, який вимагає лише одного натискання клавіші - або зовсім не (JavaScript) і просто заздалегідь визначити всі вари (як це я маю робити для E_STRICT у будь-якому випадку)!

Я не повільний машиніст - але це лише кульгавий вибір дизайну.


Перл поділяє цей біль.
Даеніт

2
Так само і C ++, і з якихось незрозумілих химерних причин PowerShell.
Рей Міясака

2
Використовуйте більш потужний редактор і визначте власні послідовності ключів для цих операторів.
кевін клайн

1
I::have::nothing::against::this->at.all()
Матін Ульхак

1
Можливо, вони не використовують клавіатури qwerty. $ і: не потрібно натискати клавішу перемикання на всіх azerty, як клавіатури.
Арх

13

Використання робочого столу надихнуло форму всередині asp.net .

Він завжди відчував хитрість і перешкоджав тому, як веб-сайт насправді працює. На щастя, asp.net-mvc не страждає так само, хоча і заслуговує на Рубі тощо за це натхнення.


18
Хіба це не бібліотека?
nikie

@nikie, це хороший момент;)
голуб

1
@nikie Насправді код ASPX на основі XML - це мова, тому ви можете скрутити цей, щоб він працював. : D
CodexArcanum

2
Справжня проблема з ASP.NET, я думаю, полягає в тому, наскільки важко він намагається приховати деталі Інтернету від програміста. Насправді в ASP.NET діють справді акуратні корисні речі, але вам доведеться боротися так сильно і копати так глибоко, щоб дістатись до нього.
CodexArcanum

1
З іншого боку, там є тисячі і тисячі простих і успішних додатків для збору даних, де їх збирають за допомогою "класичної" програми для настільних додатків. Поганим було лише те, що до MVC єдиним варіантом Microsoft були форми Windows.
ElGringoGrande

13

Для мене це абсолютна відсутність PHP умов імен і упорядкування аргументів у його стандартній бібліотеці.

Хоча необхідність JASS нівелювати посилання після звільнення / видалення посилального об'єкта (або посилання буде просочуватися і кілька байтів пам'яті втрачено) є більш серйозною, але оскільки JASS є єдиною мовою, це не так важливо.


9
Відсутність умовних умов у stdlib PHP можна стверджувати, що не є вадою дизайну мови .

3
@delnan: Відсутність конвенцій є результатом того, як PHP був розроблений, а тому має багато спільного з мовним дизайном. Крім того, мені не зрозуміло, що існує чітка відмінність між бібліотеками та мовою. Лісп, зокрема, має горду традицію завантажувати одну мову поверх іншої.
btilly

1
По-справжньому чудовою річчю JASS було те, що він мав підрахунок посилань на ручки, але не очищав би їх, якщо вони не були знищені вручну (а графічний інтерфейс створив функції, які просочували пам'ять скрізь)!
Крейг Гідні

13

Найбільший недолік дизайну, з яким я стикаюся, полягає в тому, що пітон не був розроблений, як python 3.x для початку.


1
Ну, навіть

5
@delnan, о, я знаю, і python <3 все ще є приголомшливо хорошою мовою, але трохи прикро мати кращу мову у вигляді python 3.x, яку я не можу використовувати, оскільки вона порушує всі модулі, які Мені потрібно.
dan_waterworth

Продовжуйте лобіювати свої модулі python 3.x! Тим часом я продовжую писати в 2.5.4. Завдяки ТА я насправді нагадую, що 3.x живий і здоровий.
kevpie

@kevpie По-перше, лобіюйте додати умовну компіляцію до python, щоб полегшити перехід для користувачів бібліотеки. 2to3 не є реальним рішенням у довгостроковій перспективі.
Еван Плейс

12

Розпад масиву в C і, отже, C ++.


Я також бажаю належної підтримки масиву. У C ++ ви можете запобігти занепаду, використовуючи синтаксис і шаблони abscons ... але це більше хак: /
Matthieu M.

1
Зверніть увагу , що це є причиною того, що C ++ повинен мати окремі deleteі delete[]оператор.
dan04

Ви завжди можете помістити масив у структуру і передати його за значенням, якщо вам подобається, але це зазвичай більш незручно, ніж початкова проблема. У C ++ зазвичай можна уникнути необхідності використання масивів.
Девід Торнлі

2
Принаймні, у випадку С аргументом проти належної підтримки масиву є "перевірка меж масиву дорога", особливо враховуючи спосіб роботи арифметики C покажчика.
Стівен С

@Stephen C: Яка перевірка меж масиву має відношення до розпаду масиву?
Неманья Трифунович

11

Примітивні типи на Java.

Вони порушують принцип, що все є нащадком java.lang.Object, що з теоретичної точки зору призводить до додаткової складності мовної специфікації, і з практичної точки зору вони роблять використання колекцій надзвичайно стомлюючим.

Автобоксинг допоміг полегшити практичні недоліки, але ціною зробити технічну специфікацію ще більш складною та ввести велику жирну шкіру банана: тепер ви можете отримати нульовий виняток вказівника з того, що схоже на просту арифметичну операцію.


Це може спричинити жахливі проблеми, якщо ви видалите примітивні типи. І автобоксинг можна легко зіпсувати, тому це нічого не покращує.
deadalnix

У той час це було розумне рішення від дизайнерів Java. Підвищення продуктивності даних машин / віртуальних машин, доступних у 90-х, переважало концептуальні переваги об'єднання всього навколо java.lang.Object.
mikera

10

Я найкраще знаю Перла, тому я підберу його.

Перл спробував багато ідей. Деякі були добрі. Деякі були погані. Деякі з них були оригінальними і не були широко скопійовані з поважних причин.

Одне - це ідея контексту - кожен виклик функції відбувається в списку або скалярному контексті, і може робити абсолютно різні речі в кожному контексті. Як я вказував на http://use.perl.org/~btilly/journal/36756, це ускладнює кожен API і часто призводить до тонких проблем із дизайном у коді Perl.

Наступним є ідея повного зв’язання синтаксису та типів даних. Це призводить до винаходу краватки, що дозволяє об'єктам маскуватися під інші типи даних. (Ви також можете домогтися того ж ефекту, використовуючи перевантаження, але прив'язка - це найпоширеніший підхід у Perl.)

Ще одна поширена помилка, допущена багатьма мовами, полягає в тому, щоб почати, пропонуючи динамічні рамки, а не лексичні. Пізніше повернути це дизайнерське рішення важко, і це призводить до довготривалих бородавок. Класичний опис цих бородавок в Perl - http://perl.plover.com/FAQs/Namespaces.html . Зауважте, що це було написано до того, як Perl додав ourзмінні та staticзмінні.

Люди законно не погоджуються щодо статичного та динамічного набору тексту. Мені особисто подобається динамічний набір тексту. Однак важливо мати достатню структуру для того, щоб помилки друкувати. Perl 5 добре справляється з цим суворо. Але Перл 1-4 помилився. Декілька інших мов мають шашки, які виконують те саме, що і суворі. Поки ви добре домагаєтесь перевірки ворсинок, це прийнятно.

Якщо ви шукаєте більше поганих ідей (їх багато), вивчіть PHP та вивчіть його історію. Моя улюблена помилка минулого (давно виправлена ​​через те, що вона призводила до такої кількості дірок у безпеці) була дефолтом, що дозволяла кожному встановлювати будь-яку змінну, передаючи параметри форми. Але це далеко не єдина помилка.


5
Так, Perl має багато помилок, тому що люди, які його побудували, намагалися нових ідей, і коли ти робиш це, ти часто їх неправильно помиляєш. (У Perl також є дуже непогані речі, і це стандарт для Regexps, який всі інші, здається, скопіювали)
Zachary K

@ zachary-k: Абсолютно згоден. І я спробував зробити це зрозумілим, перш ніж почати переглядати питання.
btilly

4
Спочатку Lisps були динамічно розмененими, а з часом перетворилися на лексичні рамки (принаймні, у Scheme та Common Lisp). Змінити це не неможливо.
Девід Торнлі

4
@ david-thornley: Це неможливо, якщо ви не жертвуєте десь зворотною сумісністю. Схема завжди лексично розширювалася. Звичайний Лісп був отриманий лексичним шляхом з часу його стандартизації, але різні ліспські громади боролися за його прийняття. А Emacs Lisp досі використовує динамічне оцінювання, хоча вже давно існує бажання змінити його.
btilly

1
До речі, багато речей, які люди не люблять Perl, не були винайдені в Perl, а взяті з інших мов, в основному оболонки Борна.
reinierpost

10

Неоднозначність JavaScripts для блоків коду та літералів об'єктів.

  {a:b}

може бути кодовим блоком, де aє мітка і bє виразом; або він міг би визначити об'єкт, атрибут aякого має значенняb


Мені справді подобається це у JavaScript. Простота мовної структури є приємною, і мета має бути очевидною, якщо розробник знає, що вони роблять.
Xeoncross

2
Ксеонкросс: Я взагалі не люблю двозначності. У цьому випадку для розробника це очевидно, але eval () потребує додаткових дужок.
користувач281377

2
@ammoQ: Це очевидно? Що це тоді? Об'єкт чи блок коду?
конфігуратор

конфігуратор: Очевидно, що об’єкт. Жодна розумна людина не використовує етикетку під назвою a.
користувач281377

10

Я повернусь до FORTRAN і нечутливості до білого простору.

Це пронизало специфікацію. ENDКарта повинна була бути визначена у вигляді плати з «E», з «N», і «D» в зазначеному порядку в колонках 7-72, і без будь - яких інших nonblanks, а не карти з «END» у належний стовпчики і більше нічого.

Це призвело до легкої синтаксичної плутанини. DO 100 I = 1, 10був оператором керування циклом, тоді як DO 100 I = 1. 10був оператором, який присвоював значення 1.1 змінної, що називається DO10I. (Факт, що змінні можна було створити без декларації, їх тип залежно від їх першої літери, сприяв цьому.) На відміну від інших мов, не було можливості використовувати пробіли для розділення лексем, щоб дозволити розбіжність.

Це також дозволило іншим писати дійсно заплутаний код. Є причини, чому ця функція FORTRAN ніколи не повторювалася.


1
Мовою, де ви можете переосмислити буквалі, це найгірший приклад - я маю на увазі, що це спричинило аварію лише одного крихітного космічного корабля
Мартін Бекетт

На початку ви можете написати DAMNATION замість DIMENSION, і це спрацює.
Майк Данлаве

Це все ще навчають люди поза межами КС. Мені ще доводиться мати справу з тими, хто а) бореться проти декларацій, б) бореться проти пробілів, в) на кшталт "рядків продовження", г) використовує 6-символьні імена, або 4, г) натуплюються, коли бачать (test ? a : b), д) наполягають при використанні **f) не може впоратися з чутливістю до регістру. Найбільше це було через клавіші в 50-х роках.
Майк Данлаве

1
@Martin Beckett - переосмислення літералів у FORTRAN було справді вадою компілятора, а не мовою. Це, звичайно, не було мовною особливістю умислу.
Stephen C

1
@oosterwal: Я звичайно. Я можу помилятися, але я смутно пам'ятаю визначення мови на основі перфокарт. Тоді вони були основним способом введення програм FORTRAN, а ідея 80-стовпчастої лінії зі стовпцями 73-80 зарезервована - з перфокарт.
Девід Торнлі

9

Однією з найбільших проблем BASIC була відсутність будь-якого чітко визначеного методу для розширення мови поза його ранніми середовищами, що призводить до безлічі абсолютно несумісних реалізацій (і майже неактуальної спроби постфактум для будь-якої стандартизації).

Практично будь-яка мова може отримати загальне використання деяким божевільним програмістом. Краще запланувати план використання цього загального призначення на початку, якщо випаде з розуму ідея.


2
+1: Будь-яка мова без належних модулів та бібліотек - це помилка, яка чекає цього. COBOL також постраждав від цього, що призвело до своєрідних варіантів, які не сумісні.
С.Лотт

8

Я вірю в DSL-файли (мови, що визначаються для домену), і одна річ, яку я ціную в мові, це якщо вона дозволяє мені визначати DSL поверх неї.

У Ліспі є макроси - більшість людей вважають це хорошою справою, як і я.

У C і C ++ є макроси - люди скаржаться на них, але я зміг їх використовувати для визначення DSL.

На Яві вони залишилися осторонь (а отже, і в C #), і відсутність їх була оголошена чеснотою. Звичайно, це дозволяє мати інтелігенцію, але для мене це лише твір . Щоб зробити свій DSL, я повинен розгорнутись від руки. Це біль, і це змушує мене виглядати як поганий програміст, навіть якщо це дозволяє мені робити ще куди більше з тонни коду.


4
Я погодився б, що будь-яка мова без гідних макросів - це величезний непоправний недолік дизайну. Але що ви маєте на увазі під " вони залишилися поза межами "? Препроцесор C не був пристойною макросистемою. Java не походить з будь-якої належної мови з макросами.
SK-логіка

1
Ви можете записувати DSL на зовнішній мові обробки макросу (наприклад, m4, наприклад, серед безлічі інших).
ДАЙТЕ МОЕ правильне ДУМКА

4
@SK: Я не скажу, що препроцесор C є гідною макросистемою порівняно з Lisp (наприклад). Але, порівняно ні з чим , це надзвичайно корисно.
Майк Данлаве

4
@reinierpost: Я думаю про те, що я міг би зробити в Lisp, наприклад, ввести структури управління, такі як диференційоване виконання та зворотний трек. Це можна зробити за допомогою макросів Lisp. У C / C ++ я міг зробити диференційне виконання за допомогою макросів C (і трохи дисциплінованої програмістки), але не зворотній трек. З C # я не можу зробити жодного. Я отримую в обмін такі речі, як інтелігенція. BFD.
Майк Данлаве

1
@David: У тому, що я це зробив, у мене був макрос, щоб обернути звичайний код, наприклад, список висловлювань. Це взяло б cdrсписок і створило б лямбда-закриття з нього (тобто продовження) і передало б його як аргумент до carсписку. Зрозуміло, це робилося рекурсивно, і це "зробило б правильно" для умовних, циклів та викликів функцій. Тоді функція "вибір" просто перетворилася на звичайний цикл. Не гарненько, але було міцним. Проблема в тому, що це дуже легко робити надмірно вкладені петлі.
Майк Данлаве

7

Заяви , будь-якою мовою, яка їх має. Вони нічого не роблять, що ви не можете зробити з виразами і не заважають вам робити багато речей. Існування ?:потрійного оператора - лише один приклад того, що потрібно намагатися їх обійти. У JavaScript вони особливо дратують:

// With statements:
node.listen(function(arg) {
  var result;
  if (arg) {
    result = 'yes';
  } else {
    result = 'no';
  }
  return result;
})

// Without:
node.listen(function(arg) if (arg) 'yes' else 'no')

Я тут розгублений: Ви просто хочете простіший спосіб робити речі?
TheLQ

2
Правильно. Вирази для всього.
чудовий

1
Lisp добре працює для цього.
Девід Торнлі

1
@ SK-логіка: Я підозрюю, що заяви були успадковані з машинної мови, через FORTRAN, ALGOL та COBOL.
Девід Торнлі

1
Я впевнений, що машинна мова є загальним предком, і це лише відображення того факту, що сучасні комп’ютери, засновані на архітектурі фон Неймана, виконують інструкції послідовно та змінюють стан. Зрештою, коли IO трапляється, будуть вирази, які не дають змістовних даних, тому заяви не є цілком марними, коли семантично вказують, що деякий код має лише побічні ефекти. Навіть мови, які мають поняття unitтипу (ака ()) замість висловлювань, мають особливий розгляд, щоб переконатись, що вони не кидають попереджень або інакше поводяться дивно.
Rei Miyasaka

6

Для мене проблема дизайну, яка переслідує всі мови, похідні від C; а саме - " звисаюче інше ". Ця граматична проблема повинна була бути вирішена в C ++, але вона переносилася на Java та C #.


3
Однією з головних цілей C ++ було бути повністю відсталою, сумісною з C. Якби вони різко змінили смислову поведінку, вона, можливо, не зачепилася б, як це було (або, принаймні, така думка в той час)
Ед С.

2
@Ed S., однак усунення проблеми "звисання ще" могло бути досягнуто шляхом усунення граматичного виробництва <compound_statement> (він же <block>) та включення фігурних дужок до умовних та ітеративних структур управління, як це було зроблено під час додано структуру управління обробкою виключень спробувати. Немає приводу, щоб не виправити цю граматичну неоднозначність у Java та C #. В даний час оборонна робота щодо цієї граматичної неоднозначності полягає в тому, щоб кожне твердження, що слідує за умовним або ітераційним контрольним твердженням, скласти складне твердження.
біт-твідлер

1
Що ти маєш на увазі? Це не «граматична проблема» (це абсолютно однозначно). Як би ви це "вирішили"? Я фактично вважаю, що правила в C є задовільними. Можливо, лише синтаксис, схожий на Python (= значущий відступ) може реально вирішити цю проблему. Крім того, я насправді дуже радий, що сучасні мови не вимагають дужок. Я згоден, що всі C-подібні синтаксиси смокчуть, але звисають - інакше - це найменша їхня проблема.
Конрад Рудольф

1
Продовження: Я думаю, що використання відступу Python як засобу для розмежування списку висловлювань дивно поза вірою. Ця методика порушує принцип "роз'єднання проблем" шляхом щільного з'єднання лексичного сканування із синтаксичним аналізом. Граматика без контексту повинна бути в змозі проаналізувати, не знаючи нічого про компонування джерела.
біт-трейдлер

3
@ bit-twiddler: Ні, це не так. Лексер Python просто перетворює пробіл у відповідні лексеми INDENT і DEDENT. Після цього Python має досить звичайну граматику ( docs.python.org/reference/grammar.html ).
dan04

6

Я думаю, що всі відповіді на даний момент вказують на єдиний недолік багатьох основних мов:

Немає можливості змінити основну мову, не впливаючи на зворотну сумісність.

Якщо це вирішити, то майже всі ці інші захоплення можуть бути вирішені.

EDIT.

це можна вирішити в бібліотеках, маючи різні простори імен, і ви можете уявити собі щось подібне для більшої частини ядра мови, хоча це може означати, що вам потрібно підтримати декілька компіляторів / інтерпретаторів.

Зрештою, я не думаю, що я знаю, як це вирішити таким чином, що є цілком задовільним, але це не означає, що рішення не існує, або що більше зробити не можна.


1
Як би ви вирішили "вирішити" це?
Bjarke Freund-Hansen

Я не впевнений, що це можна повністю вирішити - очевидно, що зберігання речей із основної мови та в стандартній бібліотеці допомагає, тому я думаю, я б спробував просунути це наскільки це могло йти
jk.

1
Якщо ви підтримуєте зворотну сумісність, ви маєте на увазі, що новіші компілятори повинні мати можливість складати старий код?
Рей Міясака

Я маю в виду нові компілятори не повинно змінити значення старого коду, в іншому випадку компіляції буде підмножина цього
Jk.

у більшості випадків, коли певної функції не існує або хочеться змінити, люди складають нову мову на основі попередньої. C з класами => C ++
umlcat


4

І Java, і C # мають набридливі проблеми з їх типами через бажання зберегти зворотну сумісність при додаванні загальних даних. Java не любить змішувати генеричні файли та масиви; C # не дозволить використовувати деякі корисні підписи, оскільки ти не можеш використовувати типи значень як межі.

Як приклад останнього, розглянемо це

публічний статичний T Аналіз <T> (тип <T> типу, рядок рядка), де T: Enum
поряд або заміною
Аналіз публічного статичного об'єкта (Тип типу, рядок рядка)
в Enumкласі дозволить
MyEnum e = Enum.Parse (typeof (MyEnum), str);
а не тавтологічний
MyEnum e = (MyEnum) Enum.Parse (typeof (MyEnum), str);

tl; dr: подумайте про параметричний поліморфізм, коли ви почнете розробляти систему типу, а не після публікації версії 1.


2
Неможливість обмеження типів до перерахування дратує C #, але ти можеш працювати над цим подібним чином. MyMethod<T>(T value) where T : struct, IComparable, IFormattable, IConvertible Але ти все одно повинен перевірити на перерахунок, і це хак. Я думаю, що більший брак у дженериках C # - це не підтримка вищих видів, яка б справді відкрила мову деяким крутим поняттям.
CodexArcanum

4

Я відчуваю, що я відкриваю себе, щоб отримати полум’я, але я дуже ненавиджу здатність передавати звичайні старі типи даних за посиланням на C ++. Я лише ненавиджу можливість передати складні типи за посиланням. Якщо я дивлюсь на функцію:

void foo()
{
    int a = 8;
    bar(a);
}

З точки виклику, не можна сказати, що bar, що може бути визначено у зовсім іншому файлі, є:

void bar(int& a)
{
    a++;
}

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

bar(&a);

значно читабельніший.


+1 Я не згоден з вами, але я ціную ваші міркування.
Джон Перді

@Jon Мені б дуже цікаво, що ти думаєш. Чи дотримуєтесь ви виду "не звинувачуйте мову"?
Джефф

6
@Jeff: З одного боку, основною причиною того, що еталонна семантика пробилася в C ++, було перевантаження оператором, для якого рівномірне посилання на поведінку просто має сенс. Що ще важливіше, C ++ розроблений так, щоб він був універсальним і забезпечував дуже дрібні функції, навіть якщо це несе значний ризик помилки програміста. Так що так, принаймні в цьому конкретному випадку, не звинувачуйте мову. Я скоріше зможу зробити помилки, ніж дозволити мові заважати.
Джон Перді

@Jon погодився, дуже дивно, щоб пропуск через посилання стосувався всього, крім POD. Я вважаю за краще, якщо ця функція повністю відсутня в C ++ як альтернатива, але ми можемо погодитися з цим не погоджуватися :). Дякую за вклад!
Джефф

Здається, Java не так любить покажчики, як і ви.
Mateen Ulhaq

4

АЛЬТЕР

Коли я дізнався COBOL, твердження ALTER все ще було частиною стандарту. Коротше кажучи, ця заява дозволить вам змінювати виклики процедури під час виконання.

Небезпека полягала в тому, що ви могли помістити цю заяву в якийсь незрозумілий розділ коду, до якого рідко можна було отримати доступ, і він мав потенціал повністю змінити потік решти вашої програми. За допомогою декількох заяв ALTER ви можете зробити майже неможливим дізнатися, що робить ваша програма в будь-який момент часу.

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


Хоча в цьому є корисні випадки використання - заглушення або запам'ятовування. Замість написання v() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }ви говоритеv() { result = long(operation); v = () => result; result; }
конфігуратор

4

Найгірший гріх мови програмування - це не чітке визначення. Я пам'ятаю випадок C ++, який за своїми джерелами:

  1. Був настільки жорстоко визначений, що вам не вдалося отримати програму для складання та запуску за наступними книгами чи прикладами.
  2. Після того, як ви налаштували програму на компіляцію та запуск під одним компілятором та ОС, вам доведеться почати все, якщо ви переключили компілятори чи платформи.

Як я пам’ятаю, потрібно було десятиліття, щоб C ++ визначився достатньо добре, щоб зробити його таким же професійним, як C. Це щось, що ніколи не повинно повторитися.

Щось інше, я вважаю гріхом (чи варто піти на іншу відповідь?) - це більше, ніж один "найкращий" спосіб зробити якесь спільне завдання. Це справа (знову ж таки) C ++, Perl та Ruby.


1
Я не бачу, як уникнути неправильної визначеності мовою, що розвивається. Або, з цього приводу, заздалегідь розробленою мовою, де оригінальний дизайнер пропустив деякі важливі моменти (наприклад, Паскаль).
Девід Торнлі

@David Thornley Добре визначено. Помилки, які витримують, більшість дизайнерів мови програмування розуміють це з самого початку. Інструменти можуть перевірити, що граматика є однозначною, коли вона завершена (для C ++ потрібні щонайменше три граматики), а семантика повинна бути вказана для стандартних реалізацій.
Апалала

Я погоджуюсь, що чітко визначені мови можливі, але це не відбудеться на мові, що розвивається, як попередній стандарт C ++. Альтернативою є те, що кожна мова ретельно розроблена перед випуском, і це не обов'язково спосіб отримати найкращі мови. Я б сказав, що більшість дизайнерів мов програмування помиляються на початку, оскільки дизайн мови надзвичайно складний.
Девід Торнлі

Я думаю, у мене виникають проблеми з розумінням того, що ви маєте на увазі під «чітко визначеним». Ви скаржитеся, що різні компілятори C ++ насправді не складали одну й ту ж мову?
Шон Макміллан

3

Класи на C ++ - це якась вимушена модель дизайну в мові.

Практично немає різниці між часом виконання структури і класу, і це так заплутано, щоб зрозуміти, що є справжньою перевагою програмування "приховування інформації", що я хочу її розмістити туди.

Мені до цього прийдеться, але все одно, компілятори C ++ так важко писати, що ця мова відчуває себе монстром.


2
Приховування інформації є важливим, оскільки дозволяє приховати конкретні деталі впровадження, які, ймовірно, змінюються, з доступних частин API ("інтерфейсу користувальницького інтерфейсу" API), таким чином внесення змін до програми стає простішим і менш болючим.
Анто

1
Користувацький інтерфейс API ... ні, серйозно, я його не купую.
jokoon

3
Ця різниця не є найбільш повстаючою частиною C ++, навіть не близькою. Єдина відмінність - модифікатор доступу за замовчуванням (загальнодоступний для структур, приватний для класів). C ++ - жахлива, жахлива мова, але точно не в цій частині.
SK-логіка

sk-логика: ну, я міг би сказати, що жах починається саме там.
jokoon

2
Приховування інформації добре; ви можете знайти обговорення цього в усьому. Єдиною видатною книжкою з програмного забезпечення, яку я можу придумати проти цього, був "Міфічний чоловік-місяць" Брукса, і пізніше він вважав це найбільшою помилкою книги. Якщо ви не розумієте переваг, ви насправді не кваліфіковані для того, щоб приймати рішення, яке ви виносили.
Девід Торнлі

3

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

Складний синтаксис у поєднанні з багатомовним API

Особливо це стосується такої мови, як Objective-C. Не тільки синтаксис є надзвичайно складним, але API використовує назви функцій, такі як:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

Я все за те, що явно і неоднозначно, але це смішно. Кожен раз, коли сідаю за допомогою xcode, я відчуваю себе n00b, і це насправді засмучує.


Синтаксис Objective-C є надзвичайно складним? Ви не бачили C ++? І власне назва методу є tableView:cellForRowAtIndexPath:, що на мою думку є дуже описовим.
праворуч

Навчіться дотикувати.
finnw

2
  1. підписані символи на С - мерзота, придумана для того, щоб математики мали великі колекції дрібних предметів
  2. Використання корпусу для перенесення смислового контенту - знову ж таки для математиків, яким не потрібно говорити і ніколи не вистачає місця для їх формул
  3. Дозволити / Звичайне присвоєння та Поставити призначення в Основних діалектах - тут я не думаю, що тут займаються математики

Я розгублений щодо коментаря, який ви підписали, чи можете ви пояснити?
Вінстон Еверт

1
Для людини, поняття (не) підписаних символів та необхідність сказати компілятору використовувати непідписані символи як за замовчуванням, безумовно, так само шалено, як і для математика, що 2! = 2, тому що другий 2 є великим, жирним. або курсивом.
Ekkehard.Horner

5
Проблема полягає в тому, що C змішує поняття "char" (тобто частина текстового рядка) та "byte" (тобто (u)int_least8_t). Підпис має ідеальний сенс для малих цілих чисел, але зовсім не має сенсу для символів.
dan04

@ dan04: Я згоден, я б хотів, щоб вони мали різні типи для малих цілих чисел (підписані та непідписані), байти та символи. Коли ви пояснюєте новачкам, що для маніпулювання необробленою пам'яттю їх потрібно передати на char*..., як C-String, вони сильно плутаються.
Матьє М.

C # отримали це право з окремими sbyte, byteі charтипів.
dan04
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.