
This one is easy:
-- | Class describing a resource of type @rsc@ class (Ord (IdOf rsc)) => Resource rsc where type IdOf rsc type LocOf rsc type CfgOf rsc retrieveLoc :: CfgOf rsc -> IdOf rsc -> LocOf rsc load :: LocOf rsc -> IO (Maybe rsc) -- ^ Called when a resource needs to be loaded unload :: rsc -> IO () -- ^ Idem for unloading
Consider this: instance Resource () where type IdOf () = Int type LocOf () = String type CfgOf () = () retrieveLoc () n = "Unit_" ++ show n load = undefined unload = undefined instance Resource Int where type IdOf () = Int type LocOf () = String type CfgOf () = () retrieveLoc () n = "Int_ " ++ show n load = undefined unload = undefined foo = retrieveLoc :: () -> Int -> String -- which retrieveLoc is called here? The problem, in case you haven't surmised it, is that retrieveLoc is ambiguous; you can never call it! There's no way to know which instance you might be referring to. You can work around it by making one of the type families into a data family (which is injective; you know that if CfgOf x = CfgOf y, then x = y). Or you can add a proxy parameter to retrieveLoc:
data Proxy a = Proxy retrieveLoc :: Proxy rsc -> CfgOf rsc -> IdOf rsc -> LocOf rsc
now:
foo = retrieveLoc (Proxy :: Proxy ())
and ghc can correctly infer foo's type as
foo :: () -> Int -> String
and foo will call the retrieveLoc from the () instance. -- ryan