Scary types #6

Closed
akaspin opened this Issue Jan 2, 2012 · 9 comments

Projects

None yet

2 participants

@akaspin
akaspin commented Jan 2, 2012

I started to translate library [couchdb-emumerator] from http-enumerator to http-conduit. However, several data types scare me.

For example, two identical functions old and new. Compare data types.

But I need something like

MonadCouch m =>
   HT.Method
-> String
-> HT.RequestHeaders
-> HT.Ascii
-> H.ResponseConsumer m b
-> H.RequestBody m
-> ResourceT m b

How can achieve this?

@snoyberg
Owner
snoyberg commented Jan 2, 2012

Here's a gist with an updated version that has that type signature: https://gist.github.com/1549443

However, my real recommendation would be:

  • Get rid of the MonadCouch typeclass, if you're just using the ReaderT instance alone.
  • If you really need the MonadCouch typeclass, give it a single superclass of ResourceIO m.
@akaspin
akaspin commented Jan 2, 2012

Thanks a lot for your attention.

@akaspin akaspin closed this Jan 2, 2012
@akaspin akaspin reopened this Jan 9, 2012
@akaspin
akaspin commented Jan 9, 2012

Err. As I understand, 'runResourceT' fires twice: before 'runReaderT' and after?

@snoyberg
Owner
snoyberg commented Jan 9, 2012

No, runResourceT should only ever run once. (Start bad analogy.) Imagine the monads as layers of an onion. You'll have a ReaderT layer around a ResourceT layer. First you'll unwrap the ReaderT with runReaderT, then unwrap ResourceT with runResourceT, i.e.:

runResourceT $ runReaderT myReader

Notice that the runResourceT appears first on the line, meaning it's being applied second. It's a bit confusing, I hope that helps.

@akaspin
akaspin commented Jan 9, 2012

Yep. I understand. But 'withManager' runs 'runResourceT' too.

@snoyberg
Owner

So then you can either use withManager $ \manager -> runReaderT (myReader manager) or skip withManager and use newManager instead.

@akaspin
akaspin commented Jan 10, 2012

But 'newManager' must be called inside 'runResourceT'. And we obtain the following stack: ResourceT -> ReaderT -> ResourceT.

But, as I understand line 117 in your gist, 'runReaderT' runs from base monad, and we get is unnecessary instance (I do not know what to call it) of 'runResourceT' in memory. This scheme is correct?

base ---> runResourceT --(lift)-> base ---> runReaderT ---> runResourceT

Consequently, for 'Manager' we need mutable variable. Here the experiment with 'ResourceT' inside 'StateT'. But I'm not sure about the safety of this approach.

@snoyberg
Owner

I understand your question to be: I want to allocate a Manager once
and reuse it through a number of calls. Is that correct?

In such a case: yes, you'll need runResourceT twice. We're doing the
exact same thing in Yesod. The approach you've taken is not safe.
Here's a possible alternative:

main = withManager $ \manager -> do
liftIO $ someCouchCode manager

someCouchCode manager = runResourceT $ runReaderT $ myCode manager

Point being: never return a manager outside of its containing
runResourceT. Does this make sense?

On Tue, Jan 10, 2012 at 7:34 AM, Alexander Dorofeev
reply@reply.github.com
wrote:

But 'newManager' must be called inside 'runResourceT'. And we obtain the following stack: ResourceT -> ReaderT -> ResourceT.

But, as I understand line 117 in your gist, 'runReaderT' runs from base monad, and we get is unnecessary instance (I do not know what to call it) of 'runResourceT' in memory. This scheme is correct?

   base ---> runResourceT --(lift)-> base ---> runReaderT ---> runResourceT

Consequently, for 'Manager' we need mutable variable. Here the experiment with 'ResourceT' inside 'StateT'. But I'm not sure about the safety of this approach.


Reply to this email directly or view it on GitHub:
#6 (comment)

@akaspin
akaspin commented Jan 10, 2012

Yes. Exactly. Thanks for detailed explanation. Conduit is awesome. Here is what I got in the end.

@akaspin akaspin closed this Jan 10, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment