concrete-typerep-0.1.0.2: Data/ConcreteTypeRep.hs
{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving, CPP #-}
{- |
This module defines 'Binary' and 'Hashable' instances for 'TypeRep'. These are defined on a newtype of 'TypeRep', namely 'ConcreteTypeRep', for two purposes:
* to avoid making orphan instances
* the 'Hashable' instance for 'ConcreteTypeRep' may not be pure enough for some people's tastes.
As usual with 'Typeable', this module will typically be used with some variant of @Data.Dynamic@. Two possible uses of this module are:
* making hashmaps: @HashMap 'ConcreteTypeRep' Dynamic@
* serializing @Dynamic@s.
-}
module Data.ConcreteTypeRep (
ConcreteTypeRep,
cTypeOf,
toTypeRep,
fromTypeRep,
) where
import Data.Typeable
#ifdef NEW_TYPEREP
import Data.Typeable.Internal
import GHC.Fingerprint.Type
#endif
import Data.Hashable
import Data.Binary
import System.IO.Unsafe
import Control.Applicative((<$>))
-- | Abstract type providing the functionality of 'TypeRep', but additionally supporting hashing and serialization.
--
-- The 'Eq' instance is just the 'Eq' instance for 'TypeRep', so an analogous guarantee holds: @'cTypeOf' a == 'cTypeOf' b@ if and only if @a@ and @b@ have the same type.
-- The hashing and serialization functions preserve this equality.
newtype ConcreteTypeRep = CTR { unCTR :: TypeRep } deriving(Eq, Typeable)
-- | \"Concrete\" version of 'typeOf'.
cTypeOf :: Typeable a => a -> ConcreteTypeRep
cTypeOf = fromTypeRep . typeOf
-- | Converts to the underlying 'TypeRep'
toTypeRep :: ConcreteTypeRep -> TypeRep
toTypeRep = unCTR
-- | Converts from the underlying 'TypeRep'
fromTypeRep :: TypeRep -> ConcreteTypeRep
fromTypeRep = CTR
-- show as a normal TypeRep
instance Show ConcreteTypeRep where
showsPrec i = showsPrec i . unCTR
-- | This instance is guaranteed to be consistent for a single run of the program, but not for multiple runs.
instance Hashable ConcreteTypeRep where
#ifdef NEW_TYPEREP
hashWithSalt salt (CTR (TypeRep (Fingerprint w1 w2) _ _)) = salt `hashWithSalt` w1 `hashWithSalt` w2
#else
hashWithSalt salt ctr = hashWithSalt salt (unsafePerformIO . typeRepKey . toTypeRep $ ctr)
#endif
------------- serialization: this uses Gökhan San's construction, from
---- http://www.mail-archive.com/haskell-cafe@haskell.org/msg41134.html
toTyConRep :: TyCon -> TyConRep
fromTyConRep :: TyConRep -> TyCon
#ifdef NEW_TYPEREP
type TyConRep = (String, String, String)
toTyConRep (TyCon _ pack mod name) = (pack, mod, name)
fromTyConRep (pack, mod, name) = mkTyCon3 pack mod name
#else
type TyConRep = String
toTyConRep = tyConString
fromTyConRep = mkTyCon
#endif
newtype SerialRep = SR (TyConRep, [SerialRep]) deriving(Binary)
toSerial :: ConcreteTypeRep -> SerialRep
toSerial (CTR t) =
case splitTyConApp t of
(con, args) -> SR (toTyConRep con, map (toSerial . CTR) args)
fromSerial :: SerialRep -> ConcreteTypeRep
fromSerial (SR (con, args)) = CTR $ mkTyConApp (fromTyConRep con) (map (unCTR . fromSerial) args)
instance Binary ConcreteTypeRep where
put = put . toSerial
get = fromSerial <$> get