Чому багато мов не підтримують названі параметри? [зачинено]


14

Я просто думав, як набагато легше буде читати код, якщо при виклику функції ви можете написати:

doFunction(param1=something, param2=somethingElse);

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

Чи є недолік у цьому, що мені не вистачає? Якщо ні, то чому багато мов цього не дозволяють?


1
чи можете ви надати мову, яка повинна (і може) вказати параметри, але її немає?
Брайан Чен

1
Я думаю, що це занадто багатослівно у багатьох випадках. Наскільки я бачив, чи використовує мова це чи ні, це залежить від того, чи вплине вона на мову позитивно чи негативно. Більшість мов у стилі С добре справляються без цього. У їхньому випадку часто буває досить очевидно, що все одно відбувається, і його відсутність справді допомагає просто зменшити безлад.
Panzercrisis

2
@SteveFallows: "Ідентифікована ідентифікація параметра" здається дивним назвою для визначення вільного API (як його назвав Мартін Фаулер) для класу будівельників. Ви можете знайти приклади тієї ж картини в одному з перших пунктів Ефективної Java Джошуа Блоха .
Джомінал

3
Це не обов'язково питання, засновані на думці
Catskul

2
Домовились. "Чому багато мов не підтримують названі параметри?" це скоріше питання історії мов, ніж думки. Було б думкою, якби хтось запитав це як "Не названі параметри краще?".
Енді Флемінг

Відповіді:


14

Визначення імен параметрів не завжди робить код більш читабельним: як приклад, ви б хотіли читати min(first=0, second=1)чи min(0, 1)?

Якщо ви приймаєте аргумент попереднього абзацу, то цілком очевидно, що вказівки імен параметрів не повинні бути обов'язковими. Чому всі мови не роблять вказані імена параметрів дійсними параметрами?

Я можу придумати щонайменше дві причини:

  • Загалом, введення другого синтаксису для вже охопленої потреби (тут, проходження параметрів) - це те, переваги якого повинні бути збалансовані з притаманною вартістю впровадженої мовної складності (як у мовній реалізації, так і в моделі розуму програміста мови) ;
  • Це робить зміна імен параметрів зміненням API перерви, що може не відповідати сподіванню розробника, що зміна імені параметра є тривіальною, нерозривною зміною;

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


6
Я регулярно кодую декількома різними мовами, майже завжди доводиться шукати параметри для простої підрядки на будь-якій мові. Це підрядка (початок, довжина) або підрядка (початок, кінець) і кінець включений у рядок?
Джеймс Андерсон

1
@JamesAnderson - Як би вирішила проблему підстрока з іменем параметрів? Вам все одно доведеться шукати параметри для того, щоб знати, як називається другий параметр (якщо обидві функції існують, і функція поліморфізму мови дозволяє параметри одного типу з різними іменами).
mouviciel

6
@mouviciel: Іменовані параметри взагалі не дуже корисні для письменника , але фантастичні для читача . Всі ми знаємо, наскільки важливі імена змінних для створення читабельного коду, а названі параметри дозволяють творцю інтерфейсу давати хороші імена параметрів, а також хороші імена функцій, полегшуючи людям, які використовують цей інтерфейс, писати читабельний код.
Фоші

@Phoshi - ти прав. Я просто забув важливість коду для читання, коли писав попередній коментар.
mouviciel

14

Названі параметри роблять код простішим для читання, складніше писати

Коли я читаю фрагмент коду, названі параметри можуть ввести контекст, який полегшує розуміння коду. Розглянемо, наприклад , цей конструктор: Color(1, 102, 205, 170). Що це означає? Дійсно, Color(alpha: 1, red: 102, green: 205, blue: 170)читати було б набагато простіше. Але на жаль, Укладач каже "ні" - він хоче Color(a: 1, r: 102, g: 205, b: 170). Під час написання коду з використанням названих параметрів ви витрачаєте зайву кількість часу на пошук точних імен - простіше забути точні назви деяких параметрів, ніж забути їх порядок.

Це колись мене покусало при використанні DateTimeAPI, який мав два класи братів та сестер за точками та тривалістю з майже однаковими інтерфейсами. Хоча DateTime->new(...)прийнято second => 30аргумент, DateTime::Duration->new(...)розшук seconds => 30та подібне для інших підрозділів. Так, це абсолютно має сенс, але це показало мені, що названі параметри - простота використання.

Погані імена навіть не роблять його легше читати

Інший приклад того, як названі параметри можуть бути поганими - це, мабуть, мова R. Цей фрагмент коду створює графік даних:

plot(plotdata$n, plotdata$mu, type="p", pch=17,  lty=1, bty="n", ann=FALSE, axes=FALSE)

Ви бачите два позиційні аргументи для рядків даних x і y , а потім список іменованих параметрів. Існує ще багато варіантів із типовими настройками, і перераховані лише ті, які за замовчуванням я хотів змінити або явно вказати. Після того, як ми ігноруємо, що цей код використовує магічні числа, і може отримати користь від використання enums (якщо R мав!), Проблема полягає в тому, що багато з цих імен параметрів є досить нерозбірливими.

  • pchнасправді є сюжетним символом, гліфом, який буде намальовано для кожної точки даних. 17це порожнє коло чи щось подібне.
  • lty- тип рядка. Ось 1суцільна лінія.
  • btyтип коробки. Встановлюючи це, щоб "n"уникнути намалювання поля навколо ділянки.
  • ann контролює появу конспектів осі.

Для тих, хто не знає, що означає кожна абревіатура, ці варіанти досить заплутані. Це також виявляє, чому R використовує такі мітки: Не як код самодокументування, а (будучи динамічно набраною мовою) як ключі для відображення значень у їх правильних змінних.

Властивості параметрів та підписів

Підписи функцій можуть мати такі властивості:

  • Аргументи можна впорядкувати чи не упорядкувати,
  • названий або неназваний,
  • обов'язкові або необов’язкові.
  • Підписи також можуть бути перевантажені за розміром або типом,
  • і може мати невизначений розмір з вараггами.

Різні мови приземляються за різними координатами цієї системи. В C аргументи впорядковані, безіменні, завжди потрібні і можуть бути вараггами. У Java ситуація схожа, за винятком того, що підписи можуть бути перевантажені. У Цілі C підписи впорядковують, називають, вимагають, і їх не можна перевантажувати, оскільки це лише синтаксичний цукор навколо С.

Мови, що динамічно набираються, з вараггами (інтерфейси командного рядка, Perl,…) можуть імітувати необов'язкові параметри з назвою. Мови з перевантаженням розміру підпису мають щось на зразок позиційних необов'язкових параметрів.

Як не реалізувати іменовані параметри

Мислюючи названі параметри, ми зазвичай припускаємо названі, необов'язкові, не упорядковані параметри. Реалізувати їх складно.

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

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

Для цього нам потрібен певний порядок щодо необов'язкових аргументів. Що робити, якщо код декларації буде змінено? Оскільки порядок не має значення, переупорядкування в декларації функції не повинно змінювати положення значень на стеку. Ми також повинні врахувати, чи можливо додавання нового необов’язкового параметра. З точки зору користувачів це виглядає так, оскільки код, який раніше не використовував цей параметр, все одно повинен працювати з новим параметром. Таким чином, це виключає замовлення, як-от використання порядку в декларації або використання алфавітного порядку.

Розглянемо це також у світлі підтипу та принципу заміщення Ліскова - у складеному висновку ті ж інструкції повинні мати можливість викликати метод на підтипі з можливо новими названими параметрами та на супертипі.

Можливі втілення

Якщо ми не можемо мати остаточного порядку, то нам потрібна якась не упорядкована структура даних.

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

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

  • Натомість ми могли б також передати розумну структуру даних, яка працює як словник названих аргументів. Це з дешевого боку, тому що набір клавіш статично відомий у місці виклику. Це дозволить створити ідеальну хеш-функцію або прорахувати трійку. Службовому листу все ще доведеться перевірити наявність усіх можливих назв параметрів, що є дещо дорогим. Щось подібне використовує Python.

Тож у більшості випадків це занадто дорого

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

  • Зробіть порядок названих парам частин підпису та забороніть пізніші зміни. Це корисно для самодокументування коду, але не допомагає з необов'язковими аргументами.
  • Передайте структурі даних ключ-значення запитувачу, який потім повинен отримати корисну інформацію. Це дуже дорого в порівнянні, і зазвичай це спостерігається лише в мовах сценаріїв без акценту на продуктивність.

Інші підводні камені

Імена змінних у оголошенні функції зазвичай мають деяке внутрішнє значення і не є частиною інтерфейсу - навіть якщо багато інструментів документації все одно їх показуватимуть. У багатьох випадках вам потрібні різні імена для внутрішньої змінної та відповідного іменного аргументу. Мови, які не дозволяють вибирати зовні видимі імена названого параметра, не отримують великої кількості з них, якщо ім'я змінної не використовується з урахуванням контексту виклику.

Проблема з емуляціями названих аргументів - відсутність статичної перевірки на стороні виклику. Це особливо легко забути, передаючи словник аргументів (дивлячись на вас, Python). Це важливо , тому що проходження словника є загальним обхідним шляхом, наприклад , в JavaScript: foo({bar: "baz", qux: 42}). Тут ні типи значень, ні наявність чи відсутність певних імен неможливо перевірити статично.

Емуляція іменованих параметрів (на статично типових мовах)

Просто використання рядків як ключів, а будь-який об’єкт як значення не дуже корисний при наявності перевірки статичного типу. Однак названі аргументи можуть бути імітовані структурами або об'єктними літералами:

// Java

static abstract class Arguments {
  public String bar = "default";
  public int    qux = 0;
}

void foo(Arguments args) {
  ...
}

/* using an initializer block */
foo(new Arguments(){{ bar = "baz"; qux = 42; }});

3
" it is easier to forget the exact names of some parameters than it is to forget their order" ... це цікаве твердження.
Catskul

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

1
+1 за загальні бали щодо витрат на впровадження та компромісів із виконанням названих параметрів. Я просто думаю, що початок втратять люди.
mtraceur

@mtraceur У вас є пункт. Тепер, через 5 років, я, мабуть, відповідь написати зовсім інакше. Якщо ви хочете, ви можете відредагувати мою відповідь, щоб видалити менш корисні речі. (Зазвичай такі правки будуть відхилені, оскільки вони суперечать намірам автора, але тут я з цим добре). Наприклад, я вважаю, що багато заданих параметрів проблеми - це лише обмеження динамічних мов, і вони повинні просто отримати тип типу. Наприклад, у JavaScript є Flow та TypeScript, у Python - MyPy. При належній інтеграції IDE, яка усуває більшість проблем із зручністю використання. Я все ще щось чекаю в Перлі.
амон

7

З тієї ж причини, що Угорська нотація більше не використовується широко; Intellisense (або його моральний еквівалент у IDE, що не належить Microsoft). Більшість сучасних IDE повідомить вам все, що потрібно знати про параметр, просто навести курсор миші на посилання параметра.

Однак, кілька мов підтримують названі параметри, включаючи C # і Delphi. У C # це необов’язково, тому вам не доведеться використовувати його, якщо цього не хочете, і є інші способи конкретного оголошення членів, наприклад, ініціалізація об'єкта.

Іменовані параметри в основному корисні, коли ви хочете вказати лише підмножину необов'язкових параметрів, причому не всі. У C # це дуже корисно, оскільки вам більше не потрібна купа перевантажених методів, щоб забезпечити програмісту цю гнучкість.


4
Python використовує названі параметри, і це чудова особливість мови. Я б хотів, щоб більше мов використовували його. Це полегшує аргументи за замовчуванням, оскільки ви більше не змушені, як у C ++, явно вказувати будь-які аргументи «до» за замовчуванням. (Як у вашому останньому абзаці, саме так python відходить без перевантаження ... Аргументи за замовчуванням та аргументи імен дозволяють вам робити все те саме.) Іменовані аргументи також сприяють більш читабельному коду. Коли у вас є щось на кшталт sin(radians=2), вам не потрібен "Intellisense".
Gort Robot

2

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

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

Зауважте, що досвідченим програмістам стало зручно проходити параметри на замовлення / слот. Ви також можете виявити, що ця ідіома цінна лише тоді, коли у вас є більше кількох аргументів (скажімо> 5/6). А оскільки велика кількість аргументів часто вказує на (надмірно) складні методи, ви можете виявити, що ця ідіома корисна лише для найскладніших методів.


2

Я думаю, що це та сама причина, чому c # популярніший за VB.net. Незважаючи на те, що VB.NET є більш "читабельним", наприклад, ви набираєте "end if" замість дужки, що закривається, це просто закінчується тим, що буде більше речей, які прошивають код, і в підсумку це ускладнює розуміння.

Виявляється, те, що полегшує розуміння коду, - це стислість. Чим менше, тим краще. Імена параметрів функції, як правило, досить очевидні, і насправді не так далеко, щоб допомогти вам зрозуміти код.


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

2

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

Мови, які важко переробляти, часто підтримують названі параметри, тому foo(1)що вони порушуються, коли підпис foo()зміниться, але foo(name:1)менше шансів зламатись, вимагаючи від програмиста менше зусиль для внесення змін foo.

Що ви робите, коли вам потрібно ввести новий параметр для наступної функції, і є сотні чи тисячі рядків коду, що викликає цю функцію?

foo(int weight, int height, int age = 0);

Більшість програмістів виконають наступне.

foo(int weight, int height, int age = 0, string gender = null);

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

Для конкретного genderзначення тепер ЗАВЕРШЕНО в заклик для age. Як приклад:

 foo(10,10,0,"female");

Програміст переглянув визначення функції, побачив, що ageмає значення за замовчуванням, 0і використав це значення.

Тепер значення за замовчуванням для ageвизначення функції абсолютно марно.

Названі параметри дозволяють вам уникнути цієї проблеми.

foo(weight: 10, height: 10, gender: "female");

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

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