Чому б не коментувати параметри функції?


28

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

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

let foo x y = x + y

Тепер швидкий огляд підказки покаже вам, що F # визначив, що ви призначені для x та y як ints. Якщо це те, що ви задумали, то все добре. Але я не знаю, чи це ви мали намір. Що робити, якщо ви створили цей код для об'єднання двох рядків разом? Або що робити, якщо я думаю, що ви, мабуть, мали намір додати пар? Або що робити, якщо мені просто не потрібно наводити курсор миші на кожен параметр функції, щоб визначити його тип?

Тепер візьмемо це як приклад:

let foo x y = "result: " + x + y

F # тепер передбачає, що ви, ймовірно, мали на меті об'єднати рядки, тому x і y визначаються як рядки. Однак, як поганий чмока, який підтримує ваш код, я можу поглянути на це і поцікавитися, чи, можливо, ви мали намір додати x та y (ints) разом, а потім додати результат до рядка для цілей інтерфейсу.

Безумовно, для таких простих прикладів можна відпустити це, але чому б не застосувати політику явного анотації типу?

let foo (x:string) (y:string) = "result: " + x + y

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

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

Отже ... чи є щось особливе у висновку про тип F #, що робить його цінною особливістю дотримуватися, коментуючи лише в разі потреби? Або експерт F # -ers звик коментувати свої параметри для нетривіальних додатків?


Помічена версія менш загальна. З тієї ж причини ви переважно використовуєте IEnumerable <> у підписах, а не SortedSet <>.
Патрік

2
@Patrick - Ні, це не так, оскільки F # визначає тип рядка. Я не змінив підпис функції, лише зробив це явним.
JDB

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

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

Відповіді:


30

Я не використовую F #, але в Haskell вважається хорошою формою для анотування (принаймні) визначень верхнього рівня, а іноді й локальних визначень, навіть якщо мова має загальноприйнятий тип висновку. Це з кількох причин:

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

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

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

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


3
Це звучить як дуже розумна і зважена відповідь.
JDB

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

4
В Haskell вважається правильним анотувати свої функції, але я вважаю, що ідіоматичні F # і OCaml схильні пропускати анотації, якщо це не потрібно для усунення неоднозначності. Це не означає, що це шкідливо (хоча синтаксис для коментування у F # хитріший, ніж у Haskell).
KChaloux

4
@KChaloux У OCaml вже є введення анотацій у .mliфайлі інтерфейсу ( ) (якщо ви пишете такі, на які вам наполегливо рекомендується. Анотації типу, як правило, не визначаються, оскільки вони будуть надлишковими для інтерфейсу.
Gilles ' ТАК - перестаньте бути зла '27

1
@Gilles @KChaloux, дійсно, однаковий у .fsiфайлах підпису (# ) F # , з одним застереженням: F # не використовує файли підписів для виводу типу, тому якщо щось у впровадженні неоднозначне, вам доведеться все-таки анотувати його ще раз. books.google.ca/…
Yawar Amin

1

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

Останнім часом я працюю з розбором за допомогою парсерних комбінаторів. Якщо ви знаєте синтаксичний аналіз, то знаєте, що зазвичай ви використовуєте лексери в першій фазі та аналізатор у другій фазі. Лексер перетворює текст у лексеми, а аналізатор перетворює лексеми в AST. Тепер, коли F # є функціональною мовою, а комбінатори призначені для комбінування, комбінатори парсерів призначені для використання однакових функцій як у лексері, так і в аналізаторі, але якщо ви встановите тип для функцій комбінаторів парсера, ви можете використовувати їх лише для lex або для розбору та не обох.

Наприклад:

/// Parser that requires a specific item.

// a (tok : 'a) : ('a list -> 'a * 'a list)                     // generic
// a (tok : string) : (string list -> string * string list)     // string
// a (tok : token)  : (token list  -> token  * token list)      // token

або

/// Parses iterated left-associated binary operator.


// leftbin (prs : 'a -> 'b * 'c) (sep : 'c -> 'd * 'a) (cons : 'd -> 'b -> 'b -> 'b) (err : string) : ('a -> 'b * 'c)                                                                                    // generic
// leftbin (prs : string list -> string * string list) (sep : string list -> string * string list) (cons : string -> string -> string -> string) (err : string) : (string list -> string * string list)  // string
// leftbin (prs : token list  -> token  * token list)  (sep : token list  -> token  * token list)  (cons : token  -> token  -> token  -> token)  (err : string) : (token list  -> token  * token list)   // token

Оскільки код є авторським правом, я його сюди не включатиму, але він доступний у Github . Не копіюйте його сюди.

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


1
Чому б вам не написати загальну версію у функцію? Не вдалося б це зробити?
svick

@svick Я не розумію вашого запитання. Ви маєте на увазі, що я залишив типи з визначення функції, але міг би додати загальні типи до визначень функції, оскільки загальні типи не змінять значення функції? Якщо так, то причина - це більше особисті переваги. Коли я вперше розпочав роботу з F #, я їх додав. Коли я почав працювати з більш просунутими користувачами F #, вони вважали за краще залишати їх як коментарі, тому що було легше змінити код без підписів. ...
Гай Кодер

Зрештою, я показую їх, як коментарі працювали для всіх, коли код працював. Коли я починаю писати функцію, я вкладаю типи. Як тільки у мене функція запрацює, я переміщу підпис типу до коментаря та видаляю якомога більше типів. Деякі рази з F # вам потрібно залишити тип, щоб допомогти зараженню. Деякий час я експериментував із створенням коментаря з дозволом і =, щоб ви могли коментувати рядок для тестування, а потім прокоментуйте його ще раз, коли закінчите, але вони виглядали нерозумно і додали дозволу і = не так складно.
Гай Кодер

Якщо ці коментарі відповідають на те, що ви просите, дайте мені знати, щоб я міг перенести їх у відповідь.
Гай Кодер

2
Тож коли ви змінюєте код, ви не змінюєте коментарі, що призводить до застарілої документації? Це не здається для мене перевагою. І я насправді не бачу, як брудний коментар кращий за безладний код. Мені здається, такий підхід поєднує недоліки двох варіантів.
svick

-8

Кількість помилок прямо пропорційна кількості символів у програмі!

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

Тож як приємно бути явним, будь-які додаткові параметри, які ви вводите, можуть призвести до помилок.


5
"Кількість помилок прямо пропорційна кількості символів у програмі!" Тож ми повинні всі перейти на APL ? Бути занадто лаконічним - це проблема, як і занадто багатослівний.
svick

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