Pergunta

Este é um acompanhamento de Por que estou recebendo "padrões não exaustivos em função ..." quando invoco minha função de substring Haskell?

Eu entendo isso usando -Wall, O GHC pode alertar contra padrões não exaustivos. Estou me perguntando qual é a razão por não torná-lo um erro de tempo de compilação por padrão, uma vez que é sempre possível definir explicitamente uma função parcial:

f :: [a] -> [b] -> Int
f [] _  = error "undefined for empty array"
f _ []  = error "undefined for empty array"
f (_:xs) (_:ys) = length xs + length ys

A questão não é específica do GHC.

É porque ...

  • Ninguém queria aplicar um compilador Haskell para executar esse tipo de análise?
  • Uma pesquisa de padrões não exaustiva pode encontrar alguns, mas não todos os casos?
  • As funções parcialmente definidas são consideradas legítimas e usadas com frequência suficiente para não impor o tipo de construto mostrado acima? Se for esse o caso, você pode me explicar por que os padrões não exaustivos são úteis/legítimos?
Foi útil?

Solução

Há casos em que você não se importa que uma correspondência de padrões seja não exaustiva. Por exemplo, embora essa possa não ser a implementação ideal, acho que não ajudaria se não compilar:

fac 0 = 1
fac n | n > 0 = n * fac (n-1)

Que isso não é exaustivo (números negativos não correspondem a nenhum caso) não importa realmente para o uso típico da função fatorial.

Além disso, geralmente não é possível decidir para o compilador se uma correspondência de padrão for exaustiva:

mod2 :: Integer -> Integer
mod2 n | even n = 0
mod2 n | odd n  = 1

Aqui todos os casos devem ser cobertos, mas o compilador provavelmente não pode detectá -lo. Como os guardas podem ser arbitrariamente complexos, o compilador nem sempre pode decidir se os padrões são exaustivos. Claro que este exemplo seria melhor escrito com otherwise, mas acho que também deve compilar em sua forma atual.

Outras dicas

Você pode usar -Werror para transformar avisos em erros. Não sei se você pode transformar apenas os padrões não exaustivos em erros, desculpe!

Quanto à terceira parte da sua pergunta:

Às vezes, escrevo uma série de funções que tendem a trabalhar juntos e tenho propriedades que você não pode expressar facilmente em Haskell. Pelo menos algumas dessas funções tendem a ter padrões não exaustivos, geralmente os 'consumidores'. Isso surge, por exemplo, em funções que são 'meio de' inversas umas das outras.

Um exemplo de brinquedo:

duplicate :: [a] -> [a]
duplicate [] = []
duplicate (x:xs) = x : x : (duplicate xs)

removeDuplicates :: Eq a => [a] -> [a]
removeDuplicates [] = []
removeDuplicates (x:y:xs) | x == y = x : removeDuplicates xs

Agora é muito fácil ver isso removeDuplicates (duplicate as) é igual a as (Sempre que o tipo de elemento está em Eq), mas em geral duplicate (removeDuplicates bs) vai travar, porque há um número ímpar de elementos ou 2 elementos consecutivos diferem. Se não trava, é porque bs foi produzido por (ou poderia ter sido produzido por) duplicate em primeiro lugar!.

Portanto, temos as seguintes leis (não válidas Haskell):

removeDuplicates . duplicate == id
duplicate . removeDuplicates == id (for values in the range of duplicate)

Agora, se você quiser evitar padrões não exaustivos aqui, você pode fazer removeDuplicates Retorna Maybe [a], ou adicione mensagens de erro para os casos ausentes. Você poderia até fazer algo parecido

newtype DuplicatedList a = DuplicatedList [a]

duplicate :: [a] -> DuplicatedList a
removeDuplicates :: Eq a => DuplicatedList a -> [a]
-- implementations omitted

Tudo isso é necessário, porque você não pode expressar facilmente 'ser uma lista de comprimento uniforme, com pares consecutivos de elementos sendo iguais' no sistema do tipo Haskell (a menos que você seja Oleg :)

Mas se você não exportar removeDuplicates Eu acho que é perfeitamente bom usar padrões não exaustivos aqui. Assim que você o exportar, você perderá o controle sobre as entradas e terá que lidar com os casos ausentes!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top