Может ли кто-нибудь предоставить ссылку на хороший стандарт кодирования для Haskell? Я нашел это и this, но они далеки от исчерпывающих. Не говоря уже о том, что HaskellWiki включает такие «жемчужины», как «используйте классы с осторожностью» и «определение символьных инфиксных идентификаторов следует оставить только разработчикам библиотеки».
Хорошие стандарты кодирования Haskell
Ответы (5)
Действительно сложный вопрос. Надеюсь, из ваших ответов получится что-то хорошее. А пока вот каталог ошибок и других неприятных вещей, которые я обнаружил в коде для начинающих. Есть некоторое совпадение со страницей стиля Cal Tech, на которую указывает Корнель Киселевич. Некоторые из моих советов столь же расплывчаты и бесполезны, как «жемчужины» HaskellWiki, но я надеюсь, что по крайней мере это лучший совет :-)
Отформатируйте код так, чтобы он умещался в 80 столбцов. (Продвинутые пользователи могут предпочесть 87 или 88; кроме этого, настойчиво.)
Не забывайте, что
let
привязки и предложенияwhere
создают взаимно рекурсивную группу определений, не последовательность определений.Воспользуйтесь преимуществами предложений
where
, особенно их способностью видеть параметры функции, которые уже находятся в области видимости (хороший расплывчатый совет). Если вы действительно любите Haskell, ваш код должен иметь намного большеwhere
-привязок, чемlet
-привязок. Слишком многоlet
-привязок - признак непереконструированного программиста ML или программиста на Лиспе.Избегайте лишних скобок. Некоторые места, где лишние круглые скобки особенно оскорбительны:
Вокруг условия в выражении
if
(клеймит вас как непереконструированного программиста на C)Вокруг приложения-функции, которое само по себе является аргументом инфиксного оператора (Приложение-функция связывает более жестко, чем любой инфиксный оператор. Этот факт должен быть врезан в мозг каждого Хаскеллера, во многом так же, как у динозавров был APL сгорело правило сканирования справа налево.)
Помещайте пробелы вокруг инфиксных операторов. Ставьте пробел после каждой запятой в литерале кортежа.
Предпочитайте пробел между функцией и ее аргументом, даже если аргумент заключен в круглые скобки.
Разумно используйте оператор
$
, чтобы сократить количество скобок. Помните о тесной связи между$
и инфиксом.
:f $ g $ h x == (f . g . h) x == f . g . h $ x
Не упускайте из виду встроенные типы
Maybe
иEither
.Никогда не пишите
if <expression> then True else False
; правильная фраза - это просто<expression>
.Не используйте
head
илиtail
, если вы можете использовать сопоставление с образцом.Не упускайте из виду композицию функций с помощью оператора infix dot.
Осторожно используйте перенос строки. Разрывы строк могут улучшить читаемость, но есть компромисс: ваш редактор может отображать только 40–50 строк одновременно. Если вам нужно прочитать и понять большую функцию сразу, не злоупотребляйте переносами строк.
Почти всегда предпочитают
--
комментарии, идущие до конца строки,{- ... -}
комментариям. Комментарии в скобках могут быть уместны для больших заголовков, вот и все.Дайте каждой функции верхнего уровня явную сигнатуру типа.
По возможности выравнивайте
--
строк,=
знаки и даже круглые скобки и запятые, которые встречаются в соседних строках.Под влиянием центра GHC я очень умеренно предпочитаю использовать
camelCase
для экспортируемых идентификаторов иshort_name
с подчеркиванием для локальных переменных сwhere
илиlet
.
(length l) + 1
некрасиво. Приложение length
автоматически связывает более жестко, чем приложение +
, поэтому идиоматическая вещь для написания - length l + 1
. Скобки - проклятие функциональных программ.
- person Norman Ramsey; 06.01.2010
Format your code so it fits in 80 columns.
Я больше предпочитаю 120 цветов .. Кажется, ничто не помещается в 80.
- person Talvi Watia; 09.07.2010
Несколько хороших правил большого пальца imho:
- Проконсультируйтесь с HLint, чтобы убедиться, что у вас нет лишних скобок и что ваш код не лишен смысла. точка-полная.
- Avoid recreating existing library functions. Hoogle can help you find them.
- Often times existing library functions are more general than what one was going to make. For example if you want
Maybe (Maybe a) -> Maybe a
, thenjoin
does that among other things.
- Often times existing library functions are more general than what one was going to make. For example if you want
- Argument naming and documentation is important sometimes.
- For a function like
replicate :: Int -> a -> [a]
, it's pretty obvious what each of the arguments does, from their types alone. - Для функции, которая принимает несколько аргументов одного типа, например
isPrefixOf :: (Eq a) => [a] -> [a] -> Bool
, наименование / документирование аргументов более важно.
- For a function like
- Если одна функция существует только для обслуживания другой функции и в остальном бесполезна и / или трудно придумать для нее хорошее имя, то она, вероятно, должна существовать в предложении
where
вызывающего объекта, а не в области видимости модуля. - DRY
- Use Template-Haskell when appropriate.
- Связки функций типа
zip3
,zipWith3
,zip4
,zipWith4
и т. Д. Очень удобны. Вместо этого используйте стильApplicative
сZipList
s. Вероятно, вам никогда не понадобятся такие функции. - Создавать экземпляры автоматически. Пакет derive может помочь вам получить экземпляры для классов типов, таких как
Functor
(есть только один правильный способ сделать тип экземпляромFunctor
).
- Code that is more general has several benefits:
- It's more useful and reusable.
- It is less prone to bugs because there are more constraints.
- For example if you want to program
concat :: [[a]] -> [a]
, and notice how it can be more general asjoin :: Monad m => m (m a) -> m a
. There is less room for error when programmingjoin
because when programmingconcat
you can reverse the lists by mistake and injoin
there are very few things you can do.
- For example if you want to program
- При использовании одного и того же стека преобразователей монад во многих местах кода сделайте для него синоним типа. Это сделает типы короче, лаконичнее и их будет легче массово изменять.
- Остерегайтесь "ленивого ввода-вывода". Например,
readFile
на самом деле не читает содержимое файла в момент чтения файла. - Избегайте слишком большого отступа, чтобы я не мог найти код.
- If your type is logically an instance of a type-class, make it an instance.
- The instance can replace other interface functions you may have considered with familiar ones.
- Примечание. Если существует более одного логического экземпляра, создайте для них обертки newtype.
- Сделайте разные экземпляры последовательными. Было бы очень запутанно / плохо, если бы список
Applicative
вел себя какZipList
.
Мне нравится стараться организовать функции как композиции в стиле без точек, насколько это возможно, делая такие вещи, как:
func = boo . boppity . bippity . snd where boo = ... boppity = ... bippity = ...
Мне нравится использовать ($) только для того, чтобы избежать вложенных скобок или длинных скобок в выражениях.
... Я думал, что во мне есть еще несколько, да ладно
Я нашел хороший файл разметки, охватывающий почти все аспекты стиля кода haskell. Его можно использовать как шпаргалку. Вы можете найти его здесь: ссылка