Gute Haskell-Codierungsstandards

75

Könnte jemand einen Link zu einem guten Codierungsstandard für Haskell bereitstellen? Ich habe dies und das gefunden , aber sie sind alles andere als umfassend. Ganz zu schweigen davon, dass das HaskellWiki "Edelsteine" wie "Klassen mit Sorgfalt verwenden" und "Definieren symbolischer Infix-IDs sollten nur Bibliotheksautoren überlassen werden" enthält.

Alexey Romanov
quelle
1
//, Ist das nicht eine Ansichtssache?
Nathan Basanese

Antworten:

92

Wirklich schwierige Frage. Ich hoffe, Ihre Antworten ergeben etwas Gutes. In der Zwischenzeit finden Sie hier einen Katalog mit Fehlern oder anderen nervigen Dingen, die ich im Anfängercode gefunden habe. Es gibt einige Überschneidungen mit der Cal Tech-Stilseite, auf die Kornel Kisielewicz verweist. Einige meiner Ratschläge sind genauso vage und nutzlos wie die "Juwelen" von HaskellWiki, aber ich hoffe zumindest, dass es bessere Ratschläge sind :-)

  • Formatieren Sie Ihren Code so, dass er in 80 Spalten passt. (Fortgeschrittene Benutzer bevorzugen möglicherweise 87 oder 88; darüber hinaus wird es vorangetrieben.)

  • Vergessen Sie nicht , dass letBindungen und whereKlauseln eine für beide Seiten rekursive Nest von Definitionen zu erstellen, nicht eine Folge von Definitionen.

  • Nutzen Sie whereKlauseln, insbesondere ihre Fähigkeit, bereits im Geltungsbereich befindliche Funktionsparameter zu sehen (nette vage Ratschläge). Wenn Sie wirklich Haskell grokken, sollte Ihr Code viel mehr whereBindungen als letBindungen haben. Zu viele letBindungen sind ein Zeichen für einen nicht rekonstruierten ML-Programmierer oder Lisp-Programmierer.

  • Vermeiden Sie redundante Klammern. Einige Stellen, an denen redundante Klammern besonders anstößig sind, sind

    • Um den Zustand in einem ifAusdruck (markiert Sie als nicht rekonstruierten C-Programmierer)

    • Um eine Funktionsanwendung herum, die selbst das Argument eines Infix-Operators ist ( Funktionsanwendung bindet enger als jeder Infix-Operator . Diese Tatsache sollte in das Gehirn jedes Haskellers eingebrannt werden, ähnlich wie wir Dinosaurier die Scan-Regel von rechts nach links von APL hatten eingebrannt.)

  • Setzen Sie Leerzeichen um Infix-Operatoren. Setzen Sie nach jedem Komma ein Leerzeichen in ein Tupelliteral.

  • Bevorzugen Sie ein Leerzeichen zwischen einer Funktion und ihrem Argument, auch wenn das Argument in Klammern steht.

  • Verwenden Sie den $Operator mit Bedacht, um Klammern zu reduzieren. Beachten Sie die enge Beziehung zwischen $und Infix .:

    f $ g $ h x == (f . g . h) x == f . g . h $ x
    
  • Übersehen Sie nicht die eingebauten Maybeund EitherTypen.

  • Schreibe niemals if <expression> then True else False; Der richtige Satz ist einfach <expression>.

  • Verwenden Sie nicht headoder tailwenn Sie Mustervergleich verwenden könnten.

  • Übersehen Sie die Funktionszusammensetzung nicht mit dem Infix-Punktoperator.

  • Verwenden Sie Zeilenumbrüche vorsichtig. Zeilenumbrüche können die Lesbarkeit verbessern, es gibt jedoch einen Kompromiss: Ihr Editor zeigt möglicherweise nur 40 bis 50 Zeilen gleichzeitig an. Wenn Sie eine große Funktion auf einmal lesen und verstehen müssen, dürfen Sie Zeilenumbrüche nicht überbeanspruchen.

  • Fast immer bevorzugen Sie die --Kommentare, die bis zum Zeilenende laufen, gegenüber den {- ... -}Kommentaren. Die geschweiften Kommentare sind möglicherweise für große Überschriften geeignet - fertig.

  • Geben Sie jeder Funktion der obersten Ebene eine explizite Typensignatur.

  • Richten Sie nach Möglichkeit --Linien, =Zeichen und sogar Klammern und Kommas aus, die in benachbarten Linien vorkommen.

  • Da ich von GHC Central beeinflusst werde, bevorzuge ich die Verwendung camelCasefür exportierte Bezeichner und short_namemit Unterstrichen für lokal wheregebundene oder letgebundene Variablen.

Norman Ramsey
quelle
3
Diese Antwort gefällt mir sehr gut, aber können Sie weitere Codebeispiele bereitstellen? Ich bin immer noch nicht vollständig mit Haskell-Jargon vertraut, daher bindet mich die "Funktionsanwendung enger als jeder Infix-Operator", und einige andere Punkte lassen mich verwirrt.
CaptainCasey
2
@CaptainCasey: Ich habe angefangen, einige Beispiele hinzuzufügen, aber dann wurde die Antwort viel zu lang und schwer zu lesen. Es ist als kurze Reihe von Vorschlägen gedacht; Wenn es sich in einen echten Styleguide verwandeln soll, muss es von jemand anderem gemacht werden. Aber lassen Sie mich Ihre spezifischen Punkte wissen. Die Bindungsdichtheit bedeutet nur, dass (length l) + 1das hässlich ist. Die Anwendung von lengthbindet automatisch enger als die Anwendung von +, daher ist die idiomatische Sache zu schreiben length l + 1. Klammern sind der Fluch von Funktionsprogrammen.
Norman Ramsey
7
about: Format your code so it fits in 80 columns.Ich bevorzuge 120 col mehr .. nichts scheint jemals in 80 zu passen.
Talvi Watia
Nachdem ich die meisten Ihrer Antworten auf Haskell gelesen habe, kann ich mit Sicherheit sagen, dass Sie ein großartiger Professor sind!
Fedvasu
Ihre Stilempfehlungen sind hilfreich, aber ich würde trotzdem gerne wissen, was Sie über Code mit vielen seltsamen Operatoren wie = ??, $$, <==> denken, die vielen Haskellern zu gefallen scheinen. Ist das ein guter Stil? Für mich ist es sehr schwierig, den betroffenen Quellcode zu verstehen.
mljrg
28

Einige gute Daumenregeln imho:

  • Mit Consult HLint sicher , dass Sie redundante Klammern nicht machen müssen und dass der Code nicht sinnlos Punkt-voll ist.
  • Vermeiden Sie die Neuerstellung vorhandener Bibliotheksfunktionen. Hoogle kann Ihnen helfen, sie zu finden.
    • Oft sind vorhandene Bibliotheksfunktionen allgemeiner als das, was man machen wollte. Wenn Sie zum Beispiel wollen Maybe (Maybe a) -> Maybe a, dann jointun Sie das unter anderem.
  • Die Benennung und Dokumentation von Argumenten ist manchmal wichtig.
    • Für eine Funktion wie replicate :: Int -> a -> [a]ist es ziemlich offensichtlich, was jedes der Argumente tut, allein aufgrund ihres Typs.
    • Für eine Funktion, die mehrere Argumente desselben Typs akzeptiert, z. B. isPrefixOf :: (Eq a) => [a] -> [a] -> Boolist die Benennung / Dokumentation von Argumenten wichtiger.
  • Wenn eine Funktion nur für eine andere Funktion vorhanden ist und ansonsten nicht nützlich ist und / oder es schwierig ist, einen guten Namen dafür zu finden, sollte sie wahrscheinlich in der Aufruferklausel wherestatt im Bereich des Moduls vorhanden sein.
  • TROCKEN
    • Verwenden Sie gegebenenfalls Template-Haskell.
    • Bündel von Funktionen wie zip3, zipWith3, zip4, zipWith4, etc. ist sehr meh. Verwenden Sie stattdessen ApplicativeStil mit ZipLists. Sie brauchen solche Funktionen wahrscheinlich nie wirklich.
    • Instanzen automatisch ableiten. Das Ableitungspaket kann Ihnen dabei helfen, Instanzen für Typklassen abzuleiten, z. B. Functor(es gibt nur einen richtigen Weg, einen Typ zu einer Instanz zu machen Functor).
  • Allgemeiner Code hat mehrere Vorteile:
    • Es ist nützlicher und wiederverwendbarer.
    • Es ist weniger anfällig für Fehler, da es mehr Einschränkungen gibt.
      • Zum Beispiel, wenn Sie programmieren möchten concat :: [[a]] -> [a]und beachten, wie es allgemeiner sein kann als join :: Monad m => m (m a) -> m a. Es gibt weniger Raum für Fehler beim Programmieren, joinda Sie beim Programmieren concatdie Listen versehentlich umkehren können und joinnur sehr wenige Dinge möglich sind.
  • Wenn Sie denselben Stapel von Monadentransformatoren an vielen Stellen in Ihrem Code verwenden, erstellen Sie ein Typensynonym dafür. Dadurch werden die Typen kürzer, prägnanter und lassen sich leichter in großen Mengen ändern.
  • Vorsicht vor "faulem IO". Zum Beispiel readFileliest der Inhalt der Datei zum Zeitpunkt des Lesens der Datei nicht wirklich.
  • Vermeiden Sie es, so viel einzurücken, dass ich den Code nicht finden kann.
  • Wenn Ihr Typ logisch eine Instanz einer Typklasse ist, machen Sie ihn zu einer Instanz.
    • Die Instanz kann andere Schnittstellenfunktionen, die Sie möglicherweise in Betracht gezogen haben, durch vertraute ersetzen.
    • Hinweis: Wenn mehr als eine logische Instanz vorhanden ist, erstellen Sie Newtype-Wrapper für die Instanzen.
    • Machen Sie die verschiedenen Instanzen konsistent. Es wäre sehr verwirrend / schlecht gewesen, wenn sich die Liste so Applicativeverhalten hätte ZipList.
Yairchu
quelle
7
  • Ich versuche gerne, Funktionen so weit wie möglich als punktfreie Stilkompositionen zu organisieren, indem ich Dinge tue wie:

    func = boo . boppity . bippity . snd
        where boo = ...
              boppity = ...
              bippity = ...
    
  • Ich benutze ($) nur, um verschachtelte Parens oder lange Ausdrücke in Klammern zu vermeiden

  • ... Ich dachte, ich hätte noch ein paar in mir, na ja

jberryman
quelle
6

Ich würde vorschlagen, einen Blick auf diesen Style Checker zu werfen .

Kornel Kisielewicz
quelle
4

Ich habe eine gute Markdown-Datei gefunden, die fast alle Aspekte des Haskell-Codestils abdeckt. Es kann als Spickzettel verwendet werden. Sie finden es hier: Link

d12 gefrostet
quelle