commercialhaskell / rio

A standard library for Haskell
Other
838 stars 54 forks source link

A simple way to extend `SimpleApp`? #233

Closed kindaro closed 3 years ago

kindaro commented 3 years ago

I like SimpleApp, but I want to add some more information to the environment. This is how I thought I should go:

data App = App
  { temporaryDirectory ∷ Path Abs Dir
  , simpleApp ∷ SimpleApp
  }

class HasTemporaryDirectory environment where
  temporaryDirectoryLens ∷ Lens' environment (Path Abs Dir)

instance HasTemporaryDirectory App where
  temporaryDirectoryLens = lens temporaryDirectory \ x y → x {temporaryDirectory = y}

instance HasLogFunc App where
  logFuncL = (lens simpleApp \ x y → x {simpleApp = y}) . logFuncL @SimpleApp

instance HasProcessContext App where
  processContextL = (lens simpleApp \x y -> x { simpleApp = y }).processContextL @SimpleApp

runApp ∷ RIO App α → IO α
runApp action = do
  simpleApp ← runSimpleApp ask
  temporaryDirectory ← getTempDir
  runRIO App {..} action

main ∷ IO ( )
main = runApp do
  …

However, this gives me thread blocked indefinitely in an MVar operation. I have no MVar values in my code, and it works with runSimpleApp. So, I suppose the failure arises from my invoking rumSimpleApp ask to extract the environment.

Is this behaviour expected? Can it be fixed? Is there a better way to go about this?

kindaro commented 3 years ago

Of course, I can copy some code from RIO.Prelude.Simple and have this:

runApp ∷ RIO App α → IO α
runApp action = do
  verbose <- isJust <$> lookupEnv "RIO_VERBOSE"
  lo <- logOptionsHandle stderr verbose
  withLogFunc lo $ \lf -> do
    simpleApp <- mkSimpleApp lf Nothing
    temporaryDirectory ← getTempDir
    runRIO App {..} action

But what is the point of depending on a library if I still have to borrow code from it? Also, it is very technical and I do not really want to trust myself with this sort of things.

snoyberg commented 3 years ago

This bit of code looks incorrect to me:

runApp ∷ RIO App α → IO α
runApp action = do
  simpleApp ← runSimpleApp ask
  temporaryDirectory ← getTempDir
  runRIO App {..} action

Instead of using runSimpleApp and then allowing the result of ask to escape, something like the following would be preferable:

runApp ∷ RIO App α → IO α
runApp action = runSimpleApp $ do
  simpleApp ← ask
  temporaryDirectory ← getTempDir
  runRIO App {..} action

For the rest of the code: to be honest, I'm not that comfortable with all of the language extensions employed to make that work, so it's possible something is slipping past me.

kindaro commented 3 years ago

This works, thanks!