pattern functions haskell functional-programming pattern-matching

haskell - functions - pattern matching javascript



¿Se recomienda tener siempre coincidencias de patrones exhaustivos en Haskell, incluso en casos "imposibles"? (6)

Considero que la comprobación exhaustiva de los patrones de casos es indispensable. Intento nunca usar _ en un caso en el nivel superior, porque _ coincide con todo, y al usarlo, se vicia el valor de la comprobación de exhaustividad. Esto es menos importante con listas pero crítico importante con los tipos de datos algebraicos definidos por el usuario, porque quiero poder agregar un nuevo constructor y tener el compilador barf en todos los casos faltantes. Por esta razón, siempre compilo con -Werror activado, por lo que no hay forma de que pueda omitir un caso.

Según lo observado, su código puede ser extendido con este caso.

[] : _ -> error "this can''t happen"

Internamente, GHC tiene una función de panic que, a diferencia del error , proporcionará las coordenadas de la fuente, pero observé la implementación y no logré identificarla.

¿Se recomienda tener siempre coincidencias de patrones exhaustivos en Haskell, incluso en casos "imposibles"?

Por ejemplo, en el siguiente código, coincido con el patrón en el "acumulador" de un foldr. Estoy en completo control de los contenidos del acumulador, porque lo creo (no se me pasa como entrada, sino que se construye dentro de mi función). Por lo tanto, sé que ciertos patrones nunca deben coincidir. Si me esfuerzo por no obtener nunca el error "Las coincidencias de patrón no son exhaustivas", entonces pondría una coincidencia de patrón para ese error simplemente con el mensaje "Este patrón nunca debería ocurrir". Al igual que una afirmación en C #. No puedo pensar en otra cosa que hacer allí.

¿Qué práctica recomendaría en esta situación y por qué?

Aquí está el código:

gb_groupBy p input = foldr step [] input where step item acc = case acc of [] -> [[item]] ((x:xs):ys) -> if p x item then (item:x:xs):ys else [item]:acc

El patrón no coincidente (según lo informado por el intérprete) es:

Advertencia: Las coincidencias de patrón no son exhaustivas En una alternativa de caso: Patrones no coincidentes: []: _


El sistema de tipos es su amigo, y la advertencia le indica que su función tiene grietas. El mejor enfoque es buscar un ajuste más limpio y elegante entre los distintos tipos.

Considera la definición de groupBy de groupBy :

groupBy :: (a -> a -> Bool) -> [a] -> [[a]] groupBy _ [] = [] groupBy eq (x:xs) = (x:ys) : groupBy eq zs where (ys,zs) = span (eq x) xs


Esto es probablemente más una cuestión de estilo que cualquier otra cosa. Personalmente, pondría en una

_ -> error "Impossible! Empty list in step"

aunque solo sea para silenciar la advertencia :)


Mi punto de vista es que un caso imposible no está definido .
Si no está definido, tenemos una función para él: el astutamente llamado undefined .

Completa tu coincidencia con los gustos de:

_ -> undefined

¡Y ahí lo tienes!


Para hacer un seguimiento de mi comentario anterior, me di cuenta de que hay una forma de reconocer el caso faltante pero aún así obtener un error útil con el número de archivo / línea. Sin embargo, no es ideal, ya que solo aparecerá en versiones no optimizadas (ver here ).

... []:xs -> assert False (error "unreachable because I know everything")


Puede resolver la advertencia en este caso especial haciendo esto:

gb_groupBy p input = foldr step [] input where step item acc = case acc of [] -> [[item]] (xs:xss) -> if p (head xs) item then (item:xs):xss else [item]:acc

La coincidencia del patrón se completa, y la condición "imposible" de una lista vacía en la cabecera del acumulador causaría un error de tiempo de ejecución, pero sin advertencia.

Otra forma de ver el problema más general de los emparejamientos de patrones incompletos es verlos como un "olor de código", es decir, una indicación de que estamos tratando de resolver un problema de una manera subóptima, o no haskelliana, y tratar de reescribir nuestras funciones.

Implementar groupBy con un foldr hace que sea imposible aplicarlo a una lista infinita, que es un objetivo de diseño que las funciones de la Lista de Haskell intentan alcanzar donde sea semánticamente razonable. Considerar

take 5 $ groupBy (==) someFunctionDerivingAnInfiniteList

Si los primeros 5 grupos en igualdad de condiciones son finitos, la evaluación perezosa terminará. Esto es algo que no se puede hacer en un lenguaje estrictamente evaluado. Incluso si no trabajas con listas infinitas, escribir funciones como esta producirá un mejor rendimiento en listas largas, o evitará el desbordamiento de pila que se produce al evaluar expresiones como

take 5 $ gb_groupBy (==) [1..1000000]

En List.hs , groupBy se implementa así:

groupBy :: (a -> a -> Bool) -> [a] -> [[a]] groupBy _ [] = [] groupBy eq (x:xs) = (x:ys) : groupBy eq zs where (ys,zs) = span (eq x) xs

Esto permite al intérprete / compilador evaluar solo las partes del cómputo necesarias para el resultado. span produce un par de listas, donde la primera consiste en elementos (consecutivos) del encabezado de la lista que satisfacen un predicado, y la segunda es el resto de la lista. También está implementado para trabajar en listas infinitas.