
On Thu, Jul 23, 2009 at 6:34 PM, Patrick
LeBoutillier
showIPAddr :: (IPAddr a h m) => a -> String
but something is still missing, I get:
Could not deduce (IPAddr a b m) from the context (IPAddr a h1 m1) arising from a use of `host' at IP.hs:23:23-26 Possible fix: add (IPAddr a b m) to the context of the type signature for `showIPAddr' In the second argument of `(.)', namely `host' In the first argument of `($)', namely `show . host' In the first argument of `(++)', namely `(show . host $ a)'
It's more or less the same problem as before : there may be several instance of IPAddr for the same a (typeclass are open, so the fact that there is only one such instance in your module is not relevant) and only a appears in the context of your function, so h and m aren't sufficiently determined, it's still ambiguous (if there are several instance for the a you passed, Haskell has no idea how to choose one, and this choice could change the output). There are several solutions to this problem, one was called functional dependencies :
class (IPHost h, IPMask m) => IPAddr a h m | a -> h m where host :: a -> h mask :: a -> m
That basically say that for a given a, there is only one instance : the choice of a determine h and m. So you guarantee to Haskell you won't write several instance with the same a (it would protest if you tried to). The other, recently added in GHC, is called type family, it's an open type function (from a type to a type), that's the solution I showed you :
class (IPHost (Host a), IPMask (Mask a)) => IPAddr a where type Host a type Mask a host :: a -> Host a mask :: a -> Mask a
Host and Mask are type families, type functions. In your case I believe this solution is probably more natural since your class really has only one parameter, a, adding others to make it compile is pretty much a hack. There are other situations where functional dependencies are still more natural, or work better (both solution are deemed equivalent but there are subtle implementation differences and their semantics is often more adapted to some case, besides type (or data) families are pretty recent, though they works pretty well in 6.10, they're still evolving and being refined). Your instance would look like that :
instance IPAddr IPv4Addr IPv4Host IPv4Mask where type Host IPv4Addr = IPv4Host type Mask IPv4Addr = IPv4Mask host (IPv4Addr h _) = h mask (IPv4Addr _ m) = m
-- Jedaï