Хороші стандарти кодування Haskell


76

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


1
//, хіба це не питання думки?
Натан Басанес

Відповіді:


93

Справді важке питання. Сподіваюся, ваші відповіді виявлять щось хороше. Тим часом, ось каталог помилок чи інших прикрих речей, які я знайшов у коді для початківців. Існує деяке накладання на сторінку стилю Cal Tech, на яку вказує Корнель Кіселевич. Деякі мої поради настільки ж невизначені та марні, як "коштовні камені" HaskellWiki, але я сподіваюся, що принаймні це краща порада :-)

  • Відформатуйте свій код, щоб він містив 80 стовпців. (Досвідчені користувачі можуть віддати перевагу 87 або 88; крім того, що це вимагає).

  • Не забувайте , що letпалітурки і whereположення створюють взаємно рекурсивне гніздо визначень, НЕ послідовність визначень.

  • Скористайтеся перевагами where, особливо їх здатністю бачити параметри функцій, які вже входять до сфери застосування (приємна невизначена порада). Якщо ви справді маркуєте Haskell, ваш код повинен мати набагато більше whereприв'язок, ніж letприв'язок. Занадто багато letприв'язок - ознака нереконструйованого програміста ML або програміста Lisp.

  • Уникайте зайвих дужок. Деякі місця, де зайві дужки особливо образливі, є

    • Навколо умови у ifвиразі (клеймить вас як нереконструйованого програміста на С)

    • Навколо функціональної програми, яка сама є аргументом інфіксного оператора ( функціональна програма зв'язується сильніше, ніж будь-який інфіксний оператор . Цей факт повинен бути вписаний у мозок кожного Хаскеллера, приблизно так само, як і у нас, динозаврів, правило сканування справа наліво APL спалений.)

  • Розташуйте пробіли навколо інфіксних операторів. Поставте пробіл після кожної коми у літералі кортежу.

  • Віддайте перевагу пробілу між функцією та її аргументом, навіть якщо аргумент має дужки.

  • Використовуйте $оператор розумно, щоб скоротити дужки. Пам’ятайте про тісний взаємозв’язок між $infix і .:

    f $ g $ h x == (f . g . h) x == f . g . h $ x
    
  • Не забувайте про вбудовані Maybeта Eitherтипи.

  • Ніколи не пишіть if <expression> then True else False; правильна фраза - просто <expression>.

  • Не використовуйте headабо tailколи ви могли б використовувати відповідність шаблону.

  • Не забувайте про склад функції за допомогою оператора інфіксних крапок.

  • Обережно використовуйте розриви рядків. Розриви рядків можуть підвищити читабельність, але є компроміс: Ваш редактор може відображати лише 40–50 рядків одночасно. Якщо вам потрібно прочитати і зрозуміти велику функцію відразу, не можна надмірно використовувати розриви рядків.

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

  • Дайте кожній функції верхнього рівня явний підпис типу.

  • По можливості вирівняйте --рядки, =знаки і навіть дужки та коми, які трапляються в сусідніх рядках.

  • Під впливом, як я переживаю центральний GHC, я дуже м'яко віддаю перевагу використанню camelCaseдля експортованих ідентифікаторів та short_nameз підкресленнями для локальних whereзмінних чи letзмінних.


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

2
@CaptainCasey: Я почав додавати кілька прикладів, але потім відповідь стала занадто довгою і важкою для читання. Це означає короткий набір пропозицій; якщо це перетвориться на справжній посібник зі стилю, це повинен зробити хтось інший. Але дайте мені знати ваші конкретні моменти. Щільність скріплення просто означає, що (length l) + 1це потворно. Застосування lengthавтоматично пов'язує жорсткіше, ніж додаток +, тому ідіоматичним є те, що потрібно писати length l + 1. Дужки - це загроза функціональних програм.
Норман Ремзі

7
приблизно: Format your code so it fits in 80 columns.Я більше віддаю перевагу 120 кол .. ніби ніколи ніколи не вміщується у 80.
Тальві Ватія

прочитавши більшість ваших відповідей на Haskell, я сміливо можу сказати, що ви чудовий професор!
fedvasu

Ваші рекомендації щодо стилю корисні, але все ж я хотів би знати, що ви думаєте про код із багатьма дивними операторами, такими як = ??, $$, <==>, що, здається, подобається багатьом хаскелерам? Це хороший стиль? Для мене дуже важко зрозуміти відповідний вихідний код.
mljrg

29

Кілька хороших правил великих пальців imho:

  • Порадьтеся з HLint, щоб переконатися, що у вас немає зайвих фігурних дужок, і що ваш код не безглуздо заповнений.
  • Уникайте відтворення існуючих функцій бібліотеки. Hoogle може допомогти вам їх знайти.
    • Часто існуючі функції бібліотеки є більш загальними, ніж те, що збирався зробити. Наприклад, якщо ви хочете Maybe (Maybe a) -> Maybe a, то joinце робить серед іншого.
  • Іноді аргументи та документація важливі іноді.
    • Для такої функції replicate :: Int -> a -> [a]досить очевидно, що робить кожен з аргументів, лише на основі їх типів.
    • Для функції, яка приймає кілька аргументів одного типу, наприклад isPrefixOf :: (Eq a) => [a] -> [a] -> Bool, імена / документація аргументів важливіша.
  • Якщо одна функція існує лише для обслуговування іншої функції, і в іншому випадку не є корисною, і / або важко придумати хорошу назву для неї, тоді вона, ймовірно, повинна існувати в whereпункті свого абонента, а не в області дії модуля.
  • СУХИЙ
    • За необхідності використовуйте шаблон-Haskell.
    • Пучки функцій , таких як zip3, zipWith3, zip4, zipWith4і т.д. дуже Мех. Замість цього використовуйте Applicativeстиль із ZipLists. Напевно, вам ніколи не потрібні такі функції.
    • Виводити екземпляри автоматично. Пакет виведення може допомогти вам отримати екземпляри для класів типів, таких як Functor(існує лише один правильний спосіб зробити тип екземпляром Functor).
  • Код, який є більш загальним, має кілька переваг:
    • Це більш корисно і багаторазово.
    • Він менш схильний до помилок, оскільки існує більше обмежень.
      • Наприклад, якщо ви хочете програмувати concat :: [[a]] -> [a], і зверніть увагу, як це може бути більш загальним як join :: Monad m => m (m a) -> m a. Менше місця для помилок при програмуванні, joinоскільки під час програмування concatви можете помилково змінити списки, і joinтам дуже мало речей, які ви можете зробити.
  • Використовуючи один і той самий стек монадних трансформаторів у багатьох місцях вашого коду, створіть для нього синонім типу. Це зробить типи коротшими, стислішими та простішими для масового редагування.
  • Остерігайтеся "ледачого ІО". Наприклад, readFileнасправді не читає вміст файлу на момент його прочитання.
  • Уникайте відступу настільки, що я не можу знайти код.
  • Якщо ваш тип логічно є екземпляром класу типу, зробіть його екземпляром.
    • Екземпляр може замінити інші функції інтерфейсу, які ви вже розглядали, на звичні.
    • Примітка: Якщо існує більше одного логічного екземпляра, створіть обгортки newtype для екземплярів.
    • Зробіть різні екземпляри послідовними. Було б дуже заплутано / погано, якби список Applicativeповодився так ZipList.

7
  • Мені подобається намагатися якомога більше організувати функції як безточкові композиції, роблячи такі речі:

    func = boo . boppity . bippity . snd
        where boo = ...
              boppity = ...
              bippity = ...
    
  • Мені подобається використовувати ($) лише для того, щоб уникнути вкладених парен або довгих виразів у дужках

  • ... Я думав, що в мене є ще кілька, ну добре



4

Я знайшов хороший файл розмітки, який охоплює майже всі аспекти стилю коду haskell. Його можна використовувати як шпаргалку. Ви можете знайти його тут: посилання

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