commercialhaskell / rio

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

How to mock/parameterize handles? #211

Open NickSeagull opened 4 years ago

NickSeagull commented 4 years ago

In other libraries, one would parameterize the monad in the handle type to then mock it in any monad, like identity.

But here we use Has* env classes in order to access them, the monad is always RIO. Could you provide some insights please? 🙏

snoyberg commented 4 years ago

Why do you need identity to mock?

NickSeagull commented 4 years ago

Identity was an example, let's suppose that I wanted to use State to see that everything was called in order, or perhaps using mockazo to mock automatically

snoyberg commented 4 years ago

I don't have experience with that package. Mocking can be handled effectively by using the RIO monad itself and providing mocked functions in the Has typeclass, such as a mocked log function that writes all messages to an IORef.

NickSeagull commented 4 years ago

Yes, that was my first approach, but what about handles that depend on each other? i.e.:

data Server env = Server
  { run :: RIO env ()
  }

class HasServer env where
  -- lens signature ...

new :: HasHttp env => Server env
new = Server
  { run = do
      Http{..} <- view httpL
      Database{..} <- view databaseL
      ...
  }

When initializing the environment with all of the services, I don't see a straight forward way of doing so, because I have to build the environment like:

a <- Logger.new
runRIO a $ do
    b <- Server.new
  runRIO (a, b) $ do
    ...

Perhaps I'm missing something? The point of doing the services like that is to be able to do whitebox testing by mocking service functions out.

snoyberg commented 4 years ago

I'm afraid I don't understand the question, sorry.

NickSeagull commented 4 years ago

Essentially I'm trying to implement what's on this post. Which is the typical ReaderT application with a parameterized monad.

But I don't see how to implement it with RIO