Using existing mtl classes - Polysemy

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Sridhar Ratnakumar

Does anyone have an example of doing this? In particular looking to wrap MonadHttp from the req library, and see what it takes to handle its http errors the polysemy way.

cf. https://hackage.haskell.org/package/req-3.2.0/docs/Network-HTTP-Req.html#v:handleHttpException

Torsten Schmits

explicit dictionary passing? would be really nice to have some more infrastructure for that. no idea if ConstraintAbsorber is suitable for arbitrary classes, it looked to be a bit above my pay grade

Georgi Lyubenov // googleson78

I made an orphan to deal with this exact problem:

instance Members [Embed IO, <whatever-errors-I-want-for-http-errors>] r => MonadHttp (Sem r) where
-- something like this, don't exactly remember the IO part
-- but you need some form of it, because of the MonadIO constraint on MonadHttp

It's not very pretty, but I don't care too much, since it's "application code"

Georgi Lyubenov // googleson78

Yeah, you can probably use reflection to do this, but it would also be more complicated, so I guess I avoided it

TheMatten

Or, because req interface is pretty small, you could just define Http effect that interprets into Embed Req, which in turn can be run in e.g. Embed IO

Torsten Schmits

Georgi Lyubenov // googleson78 said:

I made an orphan to deal with this exact problem:

instance Members [Embed IO, <whatever-errors-I-want-for-http-errors>] r => MonadHttp (Sem r) where
-- something like this, don't exactly remember the IO part
-- but you need some form of it, because of the MonadIO constraint on MonadHttp

It's not very pretty, but I don't care too much, since it's "application code"

does that work in general??

Georgi Lyubenov // googleson78

what do you mean? you're having doubts whether this thing works "correctly"?

Torsten Schmits

Georgi Lyubenov // googleson78 said:

what do you mean? you're having doubts whether this thing works "correctly"?

Primarily whether it is practical or possible for a wider selection of transformers to provide an instance for Sem r. I guess stuff like MonadSnap that depend on MonadBaseControl should be problematic…
Also whether it's ergonomic to integrate this into a polysemy program. Do you have some example code?

TheMatten

Primarily whether it is practical or possible for a wider selection of transformers to provide an instance for Sem r.

Classes with fundeps like MonadReader won't really work with Sem, which has to be polymorphic in effects stack to be useful

Sridhar Ratnakumar

TheMatten said:

Or, because req interface is pretty small, you could just define Http effect that interprets into Embed Req, which in turn can be run in e.g. Embed IO

This looks like an approach that I could deal with when coming back to the code after a year without worrying about not being able to understand it.

IIUC, req is the only method I use on MonadHttp; so this Http effect would have one constructor called Req? Okay, that's worth playing with.

TheMatten

You should just need to pass all the arguments to original req

Sridhar Ratnakumar

Yea. I also have a GitHub effect. So my githubToIO function can now take the Http effect as its member.

Sridhar Ratnakumar

Oh, and I can perhaps move the API request logging effect to Http itself.

TheMatten

I wonder whether this wouldn't be possible to automate - some TH for wrapping incompatible interfaces into effect with interpreter over Embedded monad

TheMatten

Haha, I guess I will open both this and https://funprog.zulipchat.com/#narrow/stream/216942-Polysemy/topic/Plugin.20package/near/200407539 as issues so that we don't forget about them :slight_smile:

Georgi Lyubenov // googleson78

@Torsten Schmits my "example" code is not very interesting:

instance Members [Error Unauthorised, Error Unauthenticated, Error InternalServerError, Embed IO] r
  => MonadHttp (Sem r) where
  handleHttpException (VanillaHttpException (HttpExceptionRequest _ (StatusCodeException response _))) =
    case statusCode $ responseStatus response of
      401 -> throw Unauthenticated
      403 -> throw Unauthorised
      _ -> throw InternalServerError
  handleHttpException _ = throw InternalServerError

and later in my chain of interpreters I have something that is basically the same as runInputSem, in which I do req