Ответ 1
Они часто могут использоваться взаимозаменяемо, но между ними существуют значительные различия. Согласование шаблонов может возникать только в конструкторах, поэтому вычисления не могут выполняться внутри шаблона, в то время как охранники - это просто многоотводные инструкции if-else. Например, я не могу написать эквивалент шаблона следующего:
func :: Int -> Int
func x
| even x = 3 * x
| odd x = 7 * x -- alternatively "otherwise = 7 * x" to get rid of all those pesky compiler warnings
Это просто не возможно с помощью простого сопоставления шаблонов. Вы также не можете делать такие вещи, как
func :: Int -> Maybe String
func x
| x < 0 = Nothing
| x == 0 = Just "Zero"
| x < 20 = Just "Small"
| x < 100 = Just "Big"
| x < 1000 = Just "Huge"
| otherwise = Just "How did you count that high?"
И наоборот, охранники, использующие ADT, не предоставляют вам много информации без вспомогательных функций. Если бы у меня был тип
data Expr
= Literal Int
| Add Expr Expr
| Mult Expr Expr
| Negate Expr
deriving (Eq, Show)
Использование охранников для записи эквивалента
eval :: Expr -> Int
eval (Literal i) = i
eval (Add e1 e2) = eval e1 + eval e2
eval (Mult e1 e2) = eval e1 * eval e2
eval (Negate e) = negate (eval e)
было бы намного более многословным, трудным и раздражающим. На самом деле, на каком-то уровне вам придется прибегнуть к сопоставлению с образцом, чтобы сделать что-то вроде
getLiteral :: Expr -> Int
getLiteral (Literal i) = i
getLiteral _ = error "Not a literal"
Что вводит функции, которые могут error
, что плохо. В этом случае использование сопоставления шаблонов намного предпочтительнее использования защитных устройств.