<div dir="ltr">The job type is not a type in the Haskell sense. It is a just a value. Promoting it to a type just creates the problem you are trying to solve. The mechanism in Haskell that selects some value given another value is the function so why not write a function JobType -> Job -> App () that selects the right job running function for the given job type?</div><br><div class="gmail_quote"><div dir="ltr">On Wed, Jul 5, 2017 at 10:11 AM Matt <<a href="mailto:parsonsmatt@gmail.com">parsonsmatt@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">You are asking GHC to select an instance of a type class (a compile-time operation) at run-time. I believe that this is possible with something like the exinst[1] package and runtime dictionaries with the constraints[2] package, but you're getting into some fairly hairy territory. I don't think that this would end up providing a nice UX for library consumers, either, as you end up needing user-defined singleton types to lift that information into the type level -- eg, a sum type of operations that the user supports, and then an open data family (or GADT) indexed by that sum type. In order for runJob to know about it, the class definition needs to know about that index, which kind of ruins the point of the class -- if you're going to keep a closed type of operations, you might as well just have:<div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">data Job = SyncContact SyncContactJob | ImportFoo ImportFooJob</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">data SyncContactJob = SyncContactJob { userId :: UserId }<br>data ImportFooJob = ImportFooJob { fooId :: FooId }</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">runJob :: Job -> App ()</font><div><div><br></div><div>The main benefit to the existentialized approach, IMO, is when the actions are entirely derived from type class operations. Sandy Maguire gave a good talk on the approach at Lambdaconf [3]. Since much of the behavior here is ad hoc, then I think you'll get less utility out of the class.</div><div><br></div><div>We have a similar sort of framework on our projects at work, but using Amazon SQS messages instead of a database. The function has a signature like:<br><br><font face="monospace, monospace">pollSqsFor :: FromJSON a => SqsQueue -> (a -> App b) -> App ()</font><br><br></div><div>This lets us write `pollSqsFor ImportThing (thingImporter :: ThingRequest -> App ())` for the workers that end up processing it, with the polling function handling stuff like delay time, error handling, keeping the message invisible, deleting it if successful, etc. This sort of thing would be easy to port to using a database, the only difference being loading only matching rows from the database for the given type.</div><div><br></div><div><div><div>[1] exinst: <a href="https://hackage.haskell.org/package/exinst" target="_blank">https://hackage.haskell.org/package/exinst</a><br>[2] contraints: <a href="http://haddock.stackage.org/lts-5.1/constraints-0.8/Data-Constraint.html" target="_blank">http://haddock.stackage.org/lts-5.1/constraints-0.8/Data-Constraint.html</a><br>[3] some1 like you: <a href="http://reasonablypolymorphic.com/some1-like-you/#/title" target="_blank">http://reasonablypolymorphic.com/some1-like-you/#/title</a></div></div></div></div></div></div><div class="gmail_extra"><br clear="all"><div><div class="m_1719800555314062990gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div>Matt Parsons</div></div></div></div></div><div class="gmail_extra">
<br><div class="gmail_quote">On Wed, Jul 5, 2017 at 6:56 AM, Saurabh Nanda <span dir="ltr"><<a href="mailto:saurabhnanda@gmail.com" target="_blank">saurabhnanda@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Overall context: <span style="font-size:12.8px">I'm trying to port the DelayedJob library from Ruby/Rails world to Haskell. The basic idea is to serialise a job and write it to the DB, deserialise it, and run the job using a job-runner function, which is determined by the job-type.</span>
<div><span style="font-size:12.8px"><br></span></div><div><div style="font-size:12.8px">I'm using a type-class to define the custom job-runner, something on the lines of:</div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">    class (FromJSON j, ToJSON j) => DelayedJob j where</div><div style="font-size:12.8px">      runJob :: j -> AppM ()</div></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">    data SyncContactsJob = SyncContactsJob { userId :: UserId } deriving (Eq, Show, Generic, FromJSON, ToJSON)</div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">    instance DelayedJob SyncContactsJob where</div><div style="font-size:12.8px">      runJob job = do</div><div style="font-size:12.8px">        -- entire job execution logic comes here</div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">Is there **any** type-system hackery, that will let me take a runtime value, eg. "SyncContactsJob", "DoBackupJob", and use that to run the correct version of the `runJob` function? That is,  how do I write the following function:</div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">   invokeJob :: JobId -> AppM ()</div><div style="font-size:12.8px">   invokeJob jid = do</div><div style="font-size:12.8px">       jobRow <- fetchJob jid</div><div style="font-size:12.8px">       let jtype = jobRow ^. jobtype -- this will have "SyncContactsJob"</div><div style="font-size:12.8px">            jvalue = jobRow ^. jobdata -- this will have a Data.Aeson.Value</div><div style="font-size:12.8px">       runJob (......) -- QUESTION: What do I write here to make this compile?</div></div>
<br>_______________________________________________<br>
Haskell-Cafe mailing list<br>
To (un)subscribe, modify options or view archives go to:<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
Only members subscribed via the mailman list are allowed to post.<br></blockquote></div><br></div>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
To (un)subscribe, modify options or view archives go to:<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
Only members subscribed via the mailman list are allowed to post.</blockquote></div>