Polysemy: Design heuristics: Dispatcher

At some point you may have strongly typed effects:

data DocumentsEffects (d :: Type) (m :: Type -> Type) (a :: Type) where
  StoreDocument :: d -> DocumentsEffects d m ()
  ListDocuments :: DocumentsEffects d m [d]

makeSem ''DocumentsEffects

Sounds nice, until you might want to dynamically dispatch multiples types.

In order to do so, we have to rely on a GADT:

data DocumentType a where
  DTLog :: DocumentType Log
  DTReciepe :: DocumentType Recipe
  DTBill :: DocumentType Bill

Then, we need a dedicated effect:

data AnyDocumentsEffects (m :: Type -> Type) (a :: Type) where
  AnyStoreDocument :: DocumentType d -> d -> AnyDocumentsEffects m ()
  AnyListDocuments :: DocumentType d -> AnyDocumentsEffects m [d]

makeSem ''AnyDocumentsEffects

Finally, you have to prove that each effect is present:

interpretAnyDocumentsEffects :: Members '[DocumentsEffects Log, DocumentsEffects Recipe, DocumentsEffects Bill] r => InterpreterFor AnyDocumentsEffects r
interpretAnyDocumentsEffects =
  interpret $
    \case
      AnyStoreDocument DTLog x -> storeDocument x
      AnyStoreDocument DTReciepe x -> storeDocument x
      AnyStoreDocument DTBill x -> storeDocument x
      AnyListDocuments DTLog -> listDocuments @Log
      AnyListDocuments DTReciepe -> listDocuments @Recipe
      AnyListDocuments DTBill -> listDocuments @Bill

See the full the code here.