Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: Difficulty embedding the context (Eq a) into a data declaration

Tags:

haskell

I am writing a simple hash tree structure in the following program hash_lookup.hs:

module Main where

    data (Eq a) => HashTable a b = HashChildren (a, [HashTable a b]) | Hash (a, b) deriving (Show)

    getKey :: HashTable a b -> a

    getKey (HashChildren (k, hs)) = k
    getKey (Hash (k, h)) = k

    lookUp :: [a] -> HashTable a b -> Maybe (HashTable a b)

    lookUp [] table = return table
    lookUp _ (Hash _) = Nothing
    lookUp (p:path) (HashChildren (_, ts) ) = lookUp path ( head ( dropWhile (\x -> (getKey x) /= p) ts ) )

getKey is intended to retrieve the root key of a given HashTable, and lookUp takes a list of strings and is meant to follow the first path it finds until it either reaches the full path or fails (I'm aware that this isn't natural behaviour for a tree but it's what my tutorial wants).

I have two questions: 1) Why do I get an error message telling me that a /= a (from the final line) is not allowed as there is No instance for (Eq a) (the error message in terminal), despite (Eq a) in the data declaration?

2) Other than the error I'm getting and the seemingly strange behaviour of the lookup function, is this good or idiomatic Haskell?

Thanks

like image 451
GabrielG Avatar asked Jun 25 '12 16:06

GabrielG


1 Answers

One of the common "gotchas" in Haskell is that if you put class constraints on a data declaration, every function that uses the type must have the class constraints too. For this reason, your data declaration is not idiomatic in Haskell, and you should eliminate its class constraint. It doesn't gain you anything to declare the class constraint in the data declaration anyway.

Basically, function types must repeat the class constraint. How else is a user of the function to know that they must use an instance of the classes in question? Notice that you can have a function f defined in terms of a function g defined in terms of an h that's defined in terms of your HashTable a b type, such that f doesn't mention your HashTable type at all, yet because of the type dependencies it takes an argument of type a that indirectly and ultimately is used as the first parameter of your HashTable type. f's type must have the class constraint, Haskell correctly infers so, and it will reject your type annotations if they lack it.

like image 93
Luis Casillas Avatar answered Oct 29 '22 23:10

Luis Casillas