74 votes

Bonnes normes de codage Haskell

Quelqu'un pourrait-il fournir un lien vers une bonne norme de codage pour Haskell ? J'ai trouvé este y este mais ils sont loin d'être exhaustifs. Sans compter que le HaskellWiki inclut des "joyaux" tels que "utiliser les classes avec précaution" et "la définition des identificateurs symboliques infixes devrait être laissée aux seuls auteurs de bibliothèques".

91voto

Norman Ramsey Points 115730

Question vraiment difficile. J'espère que vos réponses donneront quelque chose de bon. En attendant, voici un catalogue d'erreurs ou autres choses gênantes que j'ai trouvées dans le code des débutants. Il y a un certain chevauchement avec la page de style de Cal Tech que Kornel Kisielewicz indique. Certains de mes conseils sont aussi vagues et inutiles que les "gemmes" d'HaskellWiki, mais j'espère au moins que ce sont de meilleurs conseils :-)

  • Formatez votre code pour qu'il tienne en 80 colonnes. (Les utilisateurs avancés peuvent préférer 87 ou 88 ; au-delà, c'est un peu fort).

  • N'oubliez pas que let et where créent un ensemble de définitions mutuellement récurrentes, no a séquence de définitions.

  • Profitez de where notamment leur capacité à voir les paramètres de fonction qui sont déjà dans le champ d'application (joli conseil vague). Si vous êtes vraiment à l'aise avec Haskell, votre code devrait avoir beaucoup plus de where -que let -liaisons. Trop de let est le signe d'un programmeur ML ou Lisp non-construit.

  • Évitez les parenthèses redondantes. Voici quelques endroits où les parenthèses redondantes sont particulièrement choquantes

    • Autour de la condition dans un if expression (qui vous marque en tant que programmeur C non reconstruit)

    • Autour d'une application de fonction qui est elle-même l'argument d'un opérateur infixe ( L'application de la fonction lie plus étroitement que n'importe quel opérateur infixe. . Ce fait devrait être gravé dans le cerveau de chaque Haskeller, de la même manière que nous, les dinosaures, avions gravé la règle du balayage de droite à gauche de l'APL).

  • Mettre des espaces autour des opérateurs infixes. Mettre un espace après chaque virgule dans un littéral de tuple.

  • Préférez un espace entre une fonction et son argument, même si l'argument est entre parenthèses.

  • Utilisez le $ opérateur judicieux pour réduire les parenthèses. Soyez conscient de la relation étroite entre $ et infixe . :

    f $ g $ h x == (f . g . h) x == f . g . h $ x
  • Ne négligez pas l'intégration Maybe y Either types.

  • Ne jamais écrire if <expression> then True else False ; la phrase correcte est simplement <expression> .

  • N'utilisez pas head o tail alors que vous pourriez utiliser la correspondance de motifs.

  • Ne négligez pas la composition de fonctions avec l'opérateur infixe point.

  • Utilisez les sauts de ligne avec précaution. Les sauts de ligne peuvent améliorer la lisibilité, mais il y a un compromis : votre éditeur ne peut afficher que 40 à 50 lignes à la fois. Si vous devez lire et comprendre une grande fonction en une seule fois, vous ne devez pas abuser des sauts de ligne.

  • Je préfère presque toujours le -- les commentaires qui vont jusqu'à la fin de la ligne sur le {- ... -} commentaires. Les commentaires entre crochets peuvent être appropriés pour les grands en-têtes - c'est tout.

  • Donnez à chaque fonction de haut niveau une signature de type explicite.

  • Lorsque cela est possible, alignez -- lignes, = et même les parenthèses et les virgules qui se trouvent dans des lignes adjacentes.

  • Influencé comme je le suis par GHC central, j'ai une très légère préférence pour l'utilisation de camelCase pour les identifiants exportés et short_name avec des caractères de soulignement pour le local where -ou let -les variables liées.

27voto

yairchu Points 9694

De bonnes règles du jeu, selon moi :

  • Consulter HLint pour s'assurer que vous n'avez pas d'accolades redondantes et que votre code n'est pas inutilement plein de points.
  • Évitez de recréer des fonctions de bibliothèque existantes. Hoogle peut vous aider à les trouver.
    • Souvent, les fonctions de bibliothèque existantes sont plus générales que ce que l'on voulait faire. Par exemple, si vous voulez Maybe (Maybe a) -> Maybe a alors join fait cela, entre autres choses.
  • La dénomination et la documentation des arguments sont parfois importantes.
    • Pour une fonction comme replicate :: Int -> a -> [a] Si l'on se fie à leur type, la fonction de chacun des arguments est assez évidente.
    • Pour une fonction qui prend plusieurs arguments du même type, comme isPrefixOf :: (Eq a) => [a] -> [a] -> Bool Le nom et la documentation des arguments sont plus importants.
  • Si une fonction n'existe que pour servir une autre fonction, et qu'elle n'est pas autrement utile, et/ou qu'il est difficile de lui trouver un bon nom, alors elle devrait probablement exister dans le répertoire de son appelant. where plutôt que dans la portée du module.
  • SEC
    • Utilisez Template-Haskell le cas échéant.
    • Des ensembles de fonctions comme zip3 , zipWith3 , zip4 , zipWith4 etc. sont très médiocres. Utilisez Applicative style avec ZipList à la place. Vous n'aurez probablement jamais vraiment besoin de ce genre de fonctions.
    • Dérivation automatique des instances. Le site dériver peut vous aider à dériver des instances pour des classes de type telles que Functor (il n'y a qu'une seule façon correcte de faire d'un type une instance de Functor ).
  • Un code plus général présente plusieurs avantages :
    • C'est plus utile et réutilisable.
    • Elle est moins sujette aux bogues car les contraintes sont plus nombreuses.
      • Par exemple, si vous voulez programmer concat :: [[a]] -> [a] et remarquez comment il peut être plus général en tant que join :: Monad m => m (m a) -> m a . Il y a moins de place pour l'erreur lors de la programmation join car lors de la programmation concat vous pouvez inverser les listes par erreur et dans join il y a très peu de choses que vous pouvez faire.
  • Lorsque vous utilisez la même pile de transformateurs de monades à plusieurs endroits dans votre code, créez un synonyme de type pour celle-ci. Cela rendra les types plus courts, plus concis et plus faciles à modifier en masse.
  • Méfiez-vous des "IO paresseux". Par exemple readFile ne lit pas vraiment le contenu du fichier au moment où celui-ci est lu.
  • Évitez d'indenter tellement que je ne puisse pas trouver le code.
  • Si votre type est logiquement une instance d'une classe de type, faites-en une instance.
    • L'instance peut remplacer d'autres fonctions d'interface que vous avez peut-être envisagées par des fonctions familières.
    • Remarque : s'il existe plusieurs instances logiques, créez des enveloppes de type nouveau pour les instances.
    • Assurez la cohérence des différentes instances. Cela aurait été très confus/mauvais si la liste Applicative se sont comportés comme ZipList .

6voto

Kornel Kisielewicz Points 26556

Je vous suggère de jeter un coup d'oeil à ceci contrôleur de style .

6voto

jberryman Points 6615
  • J'aime essayer d'organiser les fonctions comme des compositions de style sans points possible en faisant des choses comme :

    func = boo . boppity . bippity . snd
        where boo = ...
              boppity = ...
              bippity = ...
  • J'aime utiliser ($) uniquement pour éviter les parenthèses imbriquées ou les longues expressions entre parenthèses.

  • ... Je pensais en avoir un peu plus, mais bon...

4voto

d12frosted Points 358

J'ai trouvé un bon fichier markdown couvrant presque tous les aspects du style de code haskell. Il peut être utilisé comme antisèche. Vous pouvez le trouver ici : lien

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X