Об этом стиле:
add Zero x = x
add x y = case x of
P _ -> next $ add (prev x) y
_ -> prev $ add (next x) y
с положительной стороны, это позволяет избежать некоторые повторения, что хорошо.
С отрицательной стороны, case
выглядит не исчерпывающим с первого взгляда. Действительно, чтобы убедить себя, что совпадение шаблонов действительно является исчерпывающим, мы должны рассуждать о возможных значениях для x
в case x of
и видеть, что во время выполнения, которое не может быть Zero
, потому что это было обработано выше. Это требует гораздо более умственных усилий, чем первый фрагмент, который явно исчерпывающий.
Хуже, когда вы включаете предупреждения, как мы всегда должны делать, GHC жалуется, так как не убежден, что case
является исчерпывающим.
Лично я желаю, чтобы дизайнеры Haskell полностью запретили неисчерпаемые матчи. Я бы использовал -Werror-on-non-exhaustive-matches
, если бы он был. Я хотел бы, чтобы меня заставили писать, например.
case something of
A -> ...
B -> ...
_ -> error "the impossible happened"
, не имея последней ветви, которая молча вводится компилятором для меня.
'prev' выглядит как' prev (N x) = x; prev x = P x', но тогда это не должно быть 'add (P x) y = prev $ add x y'? – Gurkenglas
На самом деле это намного сложнее, чем это; У меня есть данные AbInt = Zero | P Nat | N Nat' где 'data Nat = One | S Nat', имитированный положительный и отрицательный с обертками 'P' и' S' соответственно, поэтому 'prev (N x) = N $ S x' и так далее. –
Возможно, вы захотите включить нуль как натуральное число, используя 'data Nat = Zero | S Nat'. Это позволяет немного более непрозрачно, но гораздо проще работать с определением 'data Int '= Z Nat Nat', поскольку вам больше не нужно явно различать положительные, отрицательные и нулевые целые числа. Например, 'next (Z a b) = Z (S a) b' и' prev (Z a b) = Z a (S b) '. Еще лучше: 'intAdd (Z a b) (Z x y) = Z (natAdd a x) (natAdd b y)' для подходящего определения natAdd :: Nat -> Nat -> Nat'. – chepner