Exceptions in Yesod

2020-05-16 13:09发布

问题:

I had made a daemon that used a very primitive form of ipc (telnet and send a String that had certain words in a certain order). I snapped out of it and am now using JSON to pass messages to a Yesod server. However, there were some things I really liked about my design, and I'm not sure what my choices are now.

Here's what I was doing:

buildManager :: Phase -> IO ()
buildManager phase = do
  let buildSeq = findSeq phase
      jid = JobID $ pack "8"
      config = MkConfig $ Just jid
  flip C.catch exceptionHandler $ 
  runReaderT (sequence_ $ buildSeq <*> stages) config
  -- ^^ I would really like to keep the above line of code, or something like it.
  return ()

each function in buildSeq looked like this

foo :: Stage -> ReaderT Config IO ()

data Config = MkConfig (Either JobID Product) BaseDir JobMap

JobMap is a TMVar Map that tracks information about current jobs.

so now, what I have are Handlers, that all look like this

foo :: Handler RepJson

foo represents a command for my daemon, each handler may have to process a different JSON object.

What I would like to do is send one JSON object that represents success, and another JSON object that espresses information about some exception.

I would like foos helper function to be able to return an Either, but I'm not sure how I get that, plus the ability to terminate evaluation of my list of actions, buildSeq.

Here's the only choice I see

1) make sure exceptionHandler is in Handler. Put JobMap in the App record. Using getYesod alter the appropriate value in JobMap indicating details about the exception, which can then be accessed by foo

Is there a better way?

What are my other choices?

Edit: For clarity, I will explain the role ofHandler RepJson. The server needs some way to accept commands such as build stop report. The client needs some way of knowing the results of these commands. I have chosen JSON as the medium with which the server and client communicate with each other. I'm using the Handler type just to manage the JSON in/out and nothing more.

回答1:

Philosophically speaking, in the Haskell/Yesod world you want to pass the values forward, rather than return them backwards. So instead of having the handlers return a value, have them call forwards to the next step in the process, which may be to generate an exception.

Remember that you can bundle any amount of future actions into a single object, so you can pass a continuation object to your handlers and foos that basically tells them, "After you are done, run this blob of code." That way they can be void and return nothing.