2011-12-17 7 views
6

Utilicé un contexto de tipo al hacer una declaración de instancia para un tipo de datos que hice.Haskell Tipo Contexto de declaración de instancia requerida en las funciones

data Set a = Insert a (Set a) | EmptySet 

instance (Show a) => Show (Set a) where 
    show x = "{" ++ show' x ++ "}" where 
     show' (Insert x EmptySet) = show x 
     show' (Insert x xs) = show x ++ ", " ++ show' xs 

instance Eq a => Eq (Set a) where 
    (Insert x xs) == (Insert y ys) = (x == y) && (xs == ys) 

Así que ahora, tengo que añadir el contexto tipo de ecuación a todas las funciones que definen esa utiliza mi tipo de lance, como tal, o me sale un error de tipo:

memberSet::Eq a =>a->Set a->Bool 
memberSet _ EmptySet = False 
memberSet x (Insert y ys) 
    | x == y = True 
    | otherwise = memberSet x ys 

subSet::Eq a=>Set a->Set a->Bool 
subSet EmptySet _ = True 
subSet (Insert a as) bs 
    | memberSet a bs = subSet as bs 
    | otherwise = False 

El error Obtengo mi apariencia como:

No instance for (Eq a) 
     arising from a use of `==' 
    In the expression: (x == y) 
    In a stmt of a pattern guard for 
       an equation for `memberSet': 
     (x == y) 
    In an equation for `memberSet': 
     memberSet x (Insert y ys) 
      | (x == y) = True 
      | otherwise = memberSet x ys 
Failed, modules loaded: none. 

¿Qué significa esto? ¿Por qué obtengo este error? Pensé que una vez que hice la declaración de la instancia, Haskell podría verificar automáticamente que las cosas que se comparan por "==" en mis funciones "memberSet" y "subSet" se verificaran automáticamente como instancias de "Eq?"

Editar para mayor claridad:

Mi problema es que no entiendo por qué los contextos tipo son necesarios en "MemberSet" y "subconjunto". Si los elimino así, no compila.

memberSet::a->Set a->Bool 
    memberSet _ EmptySet = False 
    memberSet x (Insert y ys) 
     | x == y = True 
     | otherwise = memberSet x ys 

    subSet::Set a->Set a->Bool 
    subSet EmptySet _ = True 
    subSet (Insert a as) bs 
     | memberSet a bs = subSet as bs 
     | otherwise = False 
+0

El código me da typechecks para mí.¿Qué estás dejando afuera? – bdonlan

+0

Sospecho que hay algún tipo de error bastante sutil que involucra alcance o nombres, ya que el código dado parece estar bien. –

+0

Mi pregunta no estaba clara. El código como es compilado. Me pregunto por qué no se compilará con el contexto de tipo en "memberset" y "subconjunto" que editaré. –

Respuesta

4

lo que dice su declaración de instancia es que Set a es una instancia de Eq siempre es a. Si o no a es, de hecho, una instancia de Eq es completamente otra cuestión; esto simplemente le permite comparar dos Set s con ==, mientras que en memberSet solo está comparando elementos.

Considere el tipo Integer -> Integer. Esta no es una instancia de Eq por razones que deberían ser obvias. ¿Cómo esperaría que memberSet funcione si el Set contiene elementos de ese tipo?

Me pregunto si lo que esperaba lograr aquí es garantizar que solo Set s con un tipo de elemento que es una instancia de Eq se pueda crear. Si es así, ese es un problema muy diferente, pero también innecesario: dejar la restricción Eq en las funciones usando Set s tiene el mismo propósito al final.

¿Por qué no echar un vistazo a the standard Data.Set module? Tenga en cuenta que la mayoría de sus funciones que operan en conjuntos tienen una restricción de Ord, lo que refleja el hecho de que la representación interna utilizada es un árbol de búsqueda binario.

+0

Gracias. Solo estaba tratando de familiarizarme con Haskell. Solo traduje aproximadamente una implementación de SICP. No estaba tratando de lograr nada en particular. También dejé un poco de código, quería poder comparar conjuntos para la igualdad. Es por eso que es una instancia de Eq. Para hacer eso, sin embargo, me di cuenta de todo, una tenía que ser una instancia de la ecuación –

+0

@ Josh: Bueno, entonces estás de suerte, porque usted está ahora oficialmente más familiarizado con cómo funcionan las restricciones de clase de tipo. :] Y sí, comparar dos conjuntos requiere comparar sus elementos, por lo que tienes razón sobre esa parte. –

7

Sólo por el gusto de hacerlo, se puede organizar de tal manera que las limitaciones no son necesarios en las funciones mediante el uso de un GADT:

{-# LANGUAGE GADTs #-} 
module Set where 

data Set x where 
    EmptySet :: Set a 
    Insert :: Eq a => a -> Set a -> Set a 

instance Show a => Show (Set a) where 
    show EmptySet = "{}" 
    show xs = "{" ++ show' xs ++ "}" 
     where 
     show' (Insert a EmptySet) = show a 
     show' (Insert a ys) = show a ++ ", " ++ show' ys 

instance Eq (Set a) where 
    (Insert x xs) == (Insert y ys) = x == y && xs == ys 
    EmptySet == EmptySet = True 
    _ == _ = False 

memberSet :: a -> Set a -> Bool 
memberSet x (Insert y ys) = x == y || memberSet x ys 
memberSet _ _ = False 

subSet :: Set a -> Set a -> Bool 
subSet EmptySet _ = True 
subSet (Insert a as) bs 
    | memberSet a bs = subSet as bs 
    | otherwise = False 

Al poner la restricción en el EqInsert constructor del tipo , podemos asegurarnos de que

  1. Ningún conjunto no vacío se puede construir para los tipos no en Eq.
  2. El contexto Eq (y el diccionario) está disponible cada vez que ajustamos el patrón en el constructor Insert, por lo que no es necesario mencionarlo en la firma de tipo de la función.
+0

Whoa, no tenía idea de que pudieras hacer eso ... Entonces, ¿no tengo que declarar constructores de tipo cuando hago esto? –

+0

¿Quiere decir 'no tengo que declarar clases de tipos' allí, en lugar de 'tipos de constructores'? Si es así, las restricciones que pones en un constructor de valor en un 'GADT' están disponibles cuando coinciden los patrones en ese constructor, por lo que no necesitan repetirse en la firma de tipo de la función. Por supuesto, otras restricciones deben darse en la firma. Tenga en cuenta, sin embargo, que el contexto solo está disponible en _pattern matching_, no en todos los lugares donde use valores del tipo. –

+0

@Josh: Si te estás preguntando acerca de constructores de datos como 'Insertar 'y' EmptySet', esos aún están ahí. Esta es solo una sintaxis alternativa para definirlos. –

Cuestiones relacionadas