{-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE UndecidableSuperClasses #-} {-# LANGUAGE OverloadedStrings #-} import Data.Kind (Type, Constraint) import Data.Typeable import Data.Map (Map) import Data.Text (Text) import qualified Data.Text as T import Data.Type.Equality main :: IO () main = putStrLn "hello world" -- in reality this is something else type DataID = Int type Message = Text -- Some classes class (All cs a) => HasDataID cs a | a -> cs where getDataID :: a -> DataID -- Entity data AbstractEntity (cs :: [Type -> Constraint]) = forall a. (All cs a, Show a, Typeable a) => MkEntity a -- deriving (Typeable) type family All (cs :: [Type -> Constraint]) (a :: Type) :: Constraint where All (c ': cs) a = (c a, All cs a) All '[] _ = () type Entity = AbstractEntity [HasDataID '[GetMessage], GetMessage] class GetMessage a where getMessages :: Map Int Entity -> a -> Either Text [Message] -- Entity `a` should already have the UUID instance GetMessage (AbstractEntity '[HasDataID '[GetMessage], GetMessage]) where getMessages lookup (MkEntity x) = getMessages lookup x instance HasDataID '[GetMessage] (AbstractEntity '[HasDataID '[GetMessage], GetMessage]) where getDataID (MkEntity x) = getDataID x instance Show (AbstractEntity [HasDataID '[GetMessage], GetMessage]) where show (MkEntity x) = "AbstractEntity ( " <> show x <> " )" -- Function to keep unwrapping Entity until inner type is found unwrapEntity :: Text -> Entity -> Either Text DataID unwrapEntity parent_name (MkEntity e) = -- We can always unwrap ones let childName = T.pack $ show $ typeOf e -- After unwrapping we find the inner type which we will try to cast to DataID toDataID :: Typeable b => Text -> b -> Either Text DataID toDataID childName inner = case cast inner of Nothing -> Left $ "unwrapEntity: " <> parent_name <> ": Could not cast new child " <> childName Just new_child' -> Right new_child' -- Checking if there is another layer of wrapping is_wrapped :: forall a. (Typeable a) => a -> Maybe (a :~: Entity) is_wrapped _ = eqT @a @Entity in case is_wrapped e of Just witness -> -- Entity holds a new entity, cast it with the found proof and unwrap it case castWith witness e of MkEntity e2 -> let childName2 = T.pack $ show $ typeOf e2 in toDataID childName2 e2 Nothing -> toDataID childName e