Pregunta

Vamos a considerar un tipo de datos con muchos constructores:

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

Quiero escribir una función para comprobar si dos valores son producidos con el mismo constructor:

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

El mantenimiento de sameK no es muy divertido, que no puede ser fácilmente verificado su corrección. Por ejemplo, cuando se añaden nuevos constructores a T, es fácil olvidarse de actualización sameK. Omití una línea para dar un ejemplo:

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

La cuestión es cómo evitar repetitivo en sameK? O cómo asegurarse de que cheques para todos los constructores T?


La solución que he encontrado es usar tipos de datos distintos para cada uno de los constructores, que se deriva Data.Typeable, y declarando una clase de tipo común, pero eso no me gusta esta solución, porque es mucho menos fácil de leer y de lo contrario sólo un tipo simple algebraica funciona para mí:

{-# 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
¿Fue útil?

Solución

Mira el módulo de Data.Data, la función toConstr en particular. Junto con {-# LANGUAGE DeriveDataTypeable #-} que le consiga una solución de 1 línea, que funciona para cualquier tipo que es una instancia de Data.Data. No es necesario averiguar todos SYB!

Si, por alguna razón (pegado con abrazos?), Que no es una opción, entonces aquí es un corte muy feo y muy lento. Funciona sólo si su tipo de datos es Showable. (Por ejemplo mediante el uso de deriving (Show) - lo que significa que no hay tipos de función en el interior, 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 externa de un valor T mostrándola, cortarlo en palabras y luego conseguir el primero. Doy una firma de tipo explícita por lo que no está tentado a usarlo en otros tipos (y para evadir la restricción monomorphism).

Algunas desventajas notables:

  • Esto rompe horriblemente cuando el tipo tiene constructores infijos (tales como data T2 = Eta Int | T2 :^: T2)
  • Si algunos de sus constructores tienen un prefijo común, esto se va a poner más lenta, ya que una mayor parte de las cadenas tiene que ser comparado.
  • No funciona en tipos con un show costumbre, como muchos tipos de bibliotecas.

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

Otros consejos

Otra manera posible:

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

No es ideal un error de ejecución, pero mejor que dar en silencio la respuesta equivocada.

Usted tendrá que utilizar una biblioteca genéricos como chatarra Su plancha de caldera o Uniplate hacer esto en general.

Si no quieres ser tan de mano dura, se puede utilizar la solución de Dave Hinton, junto con el acceso directo de registro vacío:

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

Por lo que no tiene que saber cuántos argumentos cada constructor tiene. Pero es evidente que todavía deja mucho que desear.

En algunos casos, "Scrap Su plancha de caldera de" ayuda voluntad biblioteca.

http://www.haskell.org/haskellwiki/Scrap_your_boilerplate

definitivamente puede utilizar los medicamentos genéricos para eliminar el texto modelo. Su código es un ejemplo de libro por qué yo (y muchos otros no utilizar el comodín _ en el nivel superior). Si bien es tedioso para escribir todos los casos, es menos tedioso que hacer frente a los errores.

En este ejemplo feliz sería no sólo utilizar la solución de Dave Hinton pero impondría un pragma en línea en el f función auxiliar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top