I understand your point Ryan, but in that case, why didn't the error occur when Resource and ResourceId were separated classes?
BTW, I assume for your Int instance of Resource, you meant:
> instance Resource Int where
> type IdOf Int = Int
> type LocOf Int = String
> type CfgOf Int = ()
> retrieveLoc () n = "Int_ " ++ show n
> load = undefined
> unload = undefined
This one is easy:
Consider this:
> -- | 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
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