tipos imprimir funciones ejemplos definir composicion como haskell types pattern-matching algebraic-data-types

imprimir - haskell ejemplos



"Coincidencia de patrones" de constructores de datos de tipo algebraico (5)

Consideremos un tipo de datos con muchos constructores:

data T = Alpha Int | Beta Int | Gamma Int Int | Delta Int

Quiero escribir una función para verificar si se producen dos valores con el mismo constructor:

sameK (Alpha _) (Alpha _) = True sameK (Beta _) (Beta _) = True sameK (Gamma _ _) (Gamma _ _) = True sameK _ _ = False

Mantener sameK no es muy divertido, no se puede verificar su corrección con facilidad. Por ejemplo, cuando se agregan nuevos constructores a T , es fácil olvidarse de actualizar sameK . Omití una línea para dar un ejemplo:

-- it’s easy to forget: -- sameK (Delta _) (Delta _) = True

La pregunta es ¿cómo evitar un sameK en sameK ? ¿O cómo asegurarse de que comprueba todos T constructores T ?

La solución alternativa que encontré es usar tipos de datos separados para cada uno de los constructores, derivar Data.Typeable y declarar una clase de tipo común, pero no me gusta esta solución, porque es mucho menos legible y, por lo demás, simplemente un tipo algebraico simple. funciona para mi:

{-# LANGUAGE DeriveDataTypeable #-} import Data.Typeable class Tlike t where value :: t -> t value = id data Alpha = Alpha Int deriving Typeable data Beta = Beta Int deriving Typeable data Gamma = Gamma Int Int deriving Typeable data Delta = Delta Int deriving Typeable instance Tlike Alpha instance Tlike Beta instance Tlike Gamma instance Tlike Delta sameK :: (Tlike t, Typeable t, Tlike t'', Typeable t'') => t -> t'' -> Bool sameK a b = typeOf a == typeOf b



Mire el módulo Data.Data, la función toConstr en particular. Junto con {-# LANGUAGE DeriveDataTypeable #-} que le proporcionará una solución de 1 línea que funciona para cualquier tipo que sea una instancia de Data.Data . ¡No necesita averiguar todo SYB!

Si, por alguna razón (¿con Abrazos?), Esa no es una opción, entonces aquí hay un truco muy feo y muy lento. Funciona solo si su tipo de datos es Show able (por ejemplo, mediante deriving (Show) , lo que significa que no hay tipos de funciones dentro, por ejemplo).

constrT :: T -> String constrT = head . words . show sameK x y = constrT x == constrT y

constrT obtiene la representación de cadena del constructor más externo de un valor T mostrándolo, cortándolo en palabras y obteniendo el primero. Doy una firma de tipo explícita para que no tengas la tentación de usarla en otros tipos (y para evadir la restricción de monomorfismo).

Algunas desventajas notables:

  • Esto se rompe horriblemente cuando tu tipo tiene constructores de infijo (como los data T2 = Eta Int | T2 :^: T2 )
  • Si algunos de sus constructores tienen un prefijo compartido, esto va a ser más lento, ya que se debe comparar una gran parte de las cadenas.
  • No funciona en tipos con un show personalizado, como muchos tipos de biblioteca.

Dicho esto, es Haskell 98 ... ¡pero eso es lo único bueno que puedo decir al respecto!


Otra posible forma:

sameK x y = f x == f y where f (Alpha _) = 0 f (Beta _) = 1 f (Gamma _ _) = 2 -- runtime error when Delta value encountered

Un error de tiempo de ejecución no es ideal, pero es mejor que dar una respuesta incorrecta en silencio.


Tendrá que utilizar una biblioteca de genéricos como Chatar su boilerplate o uniplatar para hacer esto en general.

Si no quiere ser tan torpe, puede usar la solución de Dave Hinton, junto con el atajo de registro vacío:

... where f (Alpha {}) = 0 f (Beta {}) = 1 f (Gamma {}) = 2

Por lo tanto, no es necesario saber cuántos argumentos tiene cada constructor. Pero obviamente todavía deja algo que desear.


Definitivamente puede usar los genéricos para eliminar la repetición. Su código es un ejemplo de libro de texto por el que yo (y muchos otros nunca usan el _ comodín en el nivel superior). Si bien es tedioso escribir todos los casos, es menos tedioso que lidiar con los errores.

En este feliz ejemplo, no solo utilizaría la solución de Dave Hinton, sino que lanzaría un pragma EN LÍNEA en la función auxiliar f .