Deconstructing an existential type

后端 未结 3 1848
梦谈多话
梦谈多话 2021-01-15 12:58

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

3条回答
  •  無奈伤痛
    2021-01-15 13:27

    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.

提交回复
热议问题