{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
module Ideas.Utils.Typeable
( IsTypeable, typeable
, HasTypeable(..)
, castFrom, castTo, castBetween
, gcastFrom, gcastTo, gcastBetween
, module Data.Typeable
) where
import Control.Monad
import Data.Typeable
import Unsafe.Coerce
newtype IsTypeable a = IT TypeRep
class HasTypeable f where
getTypeable :: f a -> Maybe (IsTypeable a)
instance HasTypeable IsTypeable where
getTypeable = Just
typeable :: forall a . Typeable a => IsTypeable a
typeable = IT (typeRep (Proxy :: Proxy a))
castFrom :: (HasTypeable f, Typeable b) => f a -> a -> Maybe b
castFrom = flip castBetween typeable
castTo :: (HasTypeable f, Typeable a) => f b -> a -> Maybe b
castTo = castBetween typeable
castBetween :: (HasTypeable f, HasTypeable g) => f a -> g b -> a -> Maybe b
castBetween x y a = do
guardEq x y
return $ unsafeCoerce a
gcastFrom :: (HasTypeable f, Typeable b) => f a -> c a -> Maybe (c b)
gcastFrom = flip gcastBetween typeable
gcastTo :: (HasTypeable f, Typeable a) => f b -> c a -> Maybe (c b)
gcastTo = gcastBetween typeable
gcastBetween :: (HasTypeable f, HasTypeable g) => f a -> g b -> c a -> Maybe (c b)
gcastBetween ta tb x = fmap (\Refl -> x) (eqIT ta tb)
eqIT :: (HasTypeable f, HasTypeable g) => f a -> g b -> Maybe (a :~: b)
eqIT x y = do
guardEq x y
return $ unsafeCoerce Refl
guardEq :: (HasTypeable f, HasTypeable g) => f a -> g b -> Maybe ()
guardEq x y = do
IT ta <- getTypeable x
IT tb <- getTypeable y
guard (ta == tb)