I am using an existential type as a wrapper. At a point in my code where I know the enclosed type, I want to do something with it that is specific to the enclosed type. This
Use Data.Dynamic.
import Data.Dynamic
class Typeable a => Agent a where
agentId :: a -> String
-- no need for speciesId
fromAgentBox :: Agent a => AgentBox -> Maybe a
fromAgentBox (AgentBox inner) = fromDynamic (toDyn inner)
instance Agent Bug where
agentId (Bug name) = name
-- no need for speciesId
doSomethingWith :: AgentBox -> IO ()
doSomethingWith a = do
case fromAgentBox a of
Just bug -> do
-- Now the compiler knows it's a bug, and I can do something bug-specific
doBugStuff2 bug
return ()
Nothing -> return ()
Alternatively, consider declaring doSomethingWith
in the Agent
class, perhaps with a default definition.
class Agent a where
agentId :: a -> String
-- still don't need speciesId
doSomethingWith :: a -> IO ()
doSomethingWith _ = return ()
instance Agent Bug where
agentId (Bug name) = name
-- still don't need speciesId
doSomethingWith bug = do
-- Now the compiler knows it's a bug, and I can do something bug-specific
doBugStuff2 bug
return ()
Finally, I should point out that your AgentBox
type is an example of the existential typeclass anti-pattern, so you should perhaps ignore what I've written above and redesign your Agent
class as an ordinary datatype.