Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resource Management Hooks? #2

Open
ekmett opened this issue Aug 17, 2012 · 11 comments
Open

Resource Management Hooks? #2

ekmett opened this issue Aug 17, 2012 · 11 comments

Comments

@ekmett
Copy link
Owner

ekmett commented Aug 17, 2012

No description provided.

@YoEight
Copy link
Collaborator

YoEight commented Sep 24, 2012

Any preference between resourcet and regions packages ?

@ekmett
Copy link
Owner Author

ekmett commented Sep 24, 2012

The main reason this is unresolved is that I'm not a fan of either -- for different reasons.

@YoEight
Copy link
Collaborator

YoEight commented Sep 24, 2012

Could you elaborate please ? Any pointer from you would be very appreciated

@ekmett
Copy link
Owner Author

ekmett commented Sep 24, 2012

regions relies on overlapping instances, and has random unicode symbols just because it can.

While resourcet is a little more principled, it also incurs the random unicode symbols dependency and it has a million little classes with no stated rhyme or reason about how they interoperate.

@purefn
Copy link

purefn commented Feb 18, 2013

Any more thoughts on this? I'm interested in using machines for processing IO, but would like to make sure resources are only opened when needed and closed when no longer needed. I found the stream transducer described in "FP in Scala" to be quite interesting, but it seems there would need to be some significant changes to support the same mechanism.

@ekmett
Copy link
Owner Author

ekmett commented Feb 18, 2013

Paul Chiusano has done some work on bracketing resources in machines, which is what largely inspired the new 0.3 machines code that is in HEAD, but we haven't merged all of it in yet. The "FP in Scala" stream transducer and this one have been co-evolving for some time.

  1. I wrote a version a couple of years ago in Haskell.
  2. Then Runar and Paul adapted it to Scala ripping out much of the functionality, but making it usable.
  3. I went back and generalized theirs in Scala, and then transcoded it to Haskell where it could be nicer, and progressively generalized the API.
  4. Then Dan Doel and Runar ported many of those improvements back to the Scala version.
  5. Paul generalized the machines model to show we can get away with a single 'input language' parameter, came up with a radically different notion of sinks, resource control, etc.
  6. I've since incorporated some of Paul's changes into 0.3, but not yet all.

We have subtly different semantics in how we use the arguments, so I need to spend more time making sure that his combinators work under my semantics.

@ekmett
Copy link
Owner Author

ekmett commented Feb 18, 2013

If you wanted to bull ahead and take a shot at implementing them, I'd be happy to tweak any patches you come up with.

@purefn
Copy link

purefn commented Feb 18, 2013

Cool. Thanks for the history. It explains the differences very nicely.

Do you know if the work Paul did to generalize machines is available publicly? I looked at his github repo and didn't see a fork, so I was wondering if it was somewhere I didn't know to look. Thanks.

@ekmett
Copy link
Owner Author

ekmett commented Feb 19, 2013

It wasn't linked publicly but I will leak the url here. ;)

https://gist.github.com/pchiusano/4695714

@YoEight
Copy link
Collaborator

YoEight commented Mar 11, 2015

Last time I spoke with @ekmett (a year ago I guess), he wasn't a big fan of that encoding (I had a port of scalaz-stream in Haskell). Maybe we should let the inner monad handles resource management.

@HuwCampbell
Copy link
Contributor

There seems to be a problem with just letting the inner monad do it.

I've been attempting to build a library which provides safe resource handling doing just that, but I couldn't find a way to get early termination working without some form of first class support for pushing termination functions downstream.

bracketM :: MonadResource m
         => IO a                  -- ^ Acquire function
         -> (a -> IO ())          -- ^ Release
         -> (a -> MachineT m k o) -- ^ MachineT
         -> MachineT m k o
bracketM alloc free inside = MachineT $ do
    (key, seed) <- allocate alloc free
    runMachineT  $ addCleanup (release key) (inside seed)

-- | Add clean up code to run when the machine *stops*.
addCleanup :: Monad m => m () -> MachineT m k o -> MachineT m k o
addCleanup cleanup (MachineT m) = MachineT $ m >>= \x -> case x of
  -- There's a problem with this.
  -- @
  -- runT $ taking 2 <~ addCleanup (putStrLn "clean") (source [1..5])
  -- @
  -- Won't fire the cleanup. 
  Stop          -> cleanup >> return Stop
  Yield o k     -> return (Yield o (addCleanup cleanup k))
  Await g kg gg -> return (Await (addCleanup cleanup . g) kg (addCleanup cleanup gg))

-- Imagine something sensible here.
sourceFile :: ( MonadResource m, MonadIO m ) => FilePath -> SourceT m ByteString
sourceFile path =
  bracketM
    (openFile path ReadMode)
    hClose
    sourceHandle

sourceHandle :: MonadIO m => Handle -> SourceT m ByteString
sourceHandle = ...

While

foldr (starve . sourceFile) stopped ["file1.txt", "file2.txt", "file1.txt"]

behaves correctly, with only one reference to "file1.txt" being open at a time.

foldr (starve . (taking 10 <~) . sourceFile)) stopped ["file1.txt", "file2.txt", "file1.txt"]

Will have two open refs to "file1.txt", and if opened in ReadWriteMode, will cause an exception.

I did some tricks with weak refs to the cleanup function which terminate the resource on the next GC round, but that's just papering over the issue (and didn't fix this case).

Open to any ideas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants