I'm trying to implement a port of DelayedJob (from the Rails world) in Haskell.
Here's the typeclass I have which represents a DelayedJob
class (FromJSON j, ToJSON j, FromJSON r, ToJSON r) => DelayedJob j r | j -> r where
serialise :: j -> Value
serialise = toJSON
deserialise :: Value -> Either String j
deserialise = parseEither parseJSON
runJob :: j -> AppM r
Here's how I plan to use it:
createJob :: (DelayedJob j r) => j -> AppM JobId
I'm getting stuck with writing a fairly general invokeJob function which will read a row from the jobs table, look at the jobs.jobtype column and invoke the correct version of the runJob version (i.e. the runJob function belonging to the correct type-class instance).
I have the following, but it is full of boilerplate:
data JobType = SyncContacts | SendEmail | SendSms deriving (Eq, Show, Generic)
invokeJob :: JobId -> AppM ()
invokeJob jid = do
job <- fetchJob jid
case (job ^. jobtype) of
SyncContacts -> case (deserialise (job ^. jobdata) :: Either String SynContactsJob) of
Left e -> error e
Right j -> storeJobResult jid $ runAppM j
SendEmail -> case (deserialise (job ^. jobdata) :: Either String SendEmailJob) of
Left e -> error e
Right j -> storeJobResult jid $ runAppM j
SendSms -> case (deserialise (job ^. jobdata) :: Either String SendSms) of
Left e -> error e
Right j -> storeJobResult jid $ runAppM j
Essentially, is there a way to constrain the concrete type of the deserialise function dynamically during runtime without having to write so much boilerplate?
I wonder if something like this could work. I don't know the framework you are working with.
Otherwise, I'd try to see if singletons can help. Proxies could also be used if you don't want to enable ambiguous types & type applications.