über diesen Stil:
add Zero x = x
add x y = case x of
P _ -> next $ add (prev x) y
_ -> prev $ add (next x) y
Auf der positiven Seite, vermeidet es einige Wiederholungen, das ist gut.
Auf der negativen Seite, die case
scheint auf den ersten Blick nicht erschöpfend. In der Tat, um sich davon zu überzeugen, dass die Musterübereinstimmung wirklich erschöpfend ist, müssen wir über die möglichen Werte für die x
in case x of
diskutieren, und sehen, dass zur Laufzeit das Zero
nicht sein kann, weil das oben behandelt wurde. Dies erfordert weit mehr geistige Anstrengung als das erste Snippet, das offensichtlich erschöpfend ist.
Schlimmer noch, beim Einschalten von Warnungen, wie wir es immer tun sollten, beschwert sich GHC, da es nicht davon überzeugt ist, dass die case
erschöpfend ist.
Persönlich wünschte ich, die Designer von Haskell hätten nicht vollständige Spiele gänzlich verboten. Ich würde einen -Werror-on-non-exhaustive-matches
verwenden, wenn es einen gab. Ich würde gerne gezwungen werden, z.B.
als die letzte Verzweigung, die vom Compiler für mich stillgelegt wird.
'prev' sieht aus wie' prev (N x) = x; prev x = P x' aber sollte es nicht 'add (P x) sein y = prev $ add x y'? – Gurkenglas
Eigentlich ist es viel komplizierter als das; Ich habe 'Daten AbInt = Zero | P Nat | N Nat, wo 'Daten Nat = Eins | S Nat ', simuliert positiv und negativ mit' P 'und' S 'Wrapper, also' prev (N x) = N $ S x 'und so weiter. –
Sie könnten die Null als natürliche Zahl mit 'data Nat = Zero | S Nat'. Dies erlaubt eine etwas undurchsichtigere, aber wesentlich einfachere Definition von "Daten Int" = Z Nat Nat, da Sie nicht mehr explizit zwischen positiven, negativen und ganzen Zahlen Null unterscheiden müssen. Zum Beispiel ist "next (Z a b) = Z (S a) b" und "prev (Z a b) = Z a (S b)". Noch besser: 'intAdd (Z a b) (Z x y) = Z (natAdd a x) (natAdd b y)' für geeignet definierte 'natAdd :: Nat -> Nat -> Nat'. – chepner