[Haskell-cafe] Am I using type families well?

Ryan Ingram ryani.spam at gmail.com
Mon Nov 1 20:48:01 EDT 2010


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


More information about the Haskell-Cafe mailing list