0

I'm looking at the scala-arm library, prompted by this answer, and it looks great for managing resources in most contexts.

There's one context, though, that it doesn't, on first glance, appear to handle: that of "handing off" a resource to another resource. This comes up frequently when working with I/O:

for (fin <- managed(new FileInputStream(file));
     // almost what we want, except see below
     gzip <- managed(new GZIPInputStream(fin));
     src <- managed(Source.fromInputStream(gzip))) {
  /* some fancy code */
}

Now, the problem is this: If gzip is successfully created, then it is responsible for closing fin, and fin should not be closed (update: this isn't quite right - double-close is fine; see accepted answer). The alternative, though:

for (src <- managed(Source.fromInputStream(
              new GZIPInputStream(new FileInputStream(file))))) {
  /* some fancy code */
}

is not quite correct - if there is an (admittedly unlikely) error in the GZIPInputStream constructor, the FileInputStream is not closed. Ditto for fromInputStream.

Does scala-arm (or some other package) provide a facility for handling this cleanup safely that I haven't found yet?

Community
  • 1
  • 1
Michael Ekstrand
  • 28,379
  • 9
  • 61
  • 93

2 Answers2

3

Did a few more minutes of looking, and found that it really doesn't matter. java.io.Closeable stipulates that close()ing an already-closed resource is a no-op. So it's safe to wrap everything in managed and let it double-close. The first code example, therefore, is correct.

Michael Ekstrand
  • 28,379
  • 9
  • 61
  • 93
1

The best approach to me is to use the notion of iteratee, enumerator and enumeratee that has its fundation in the functional world.

What you might be interested in is the Scalaz library that have a very good implementation.

In some words those concepts are used in order to have both the iterator on the source knowing when the consummator had ended consumming but also the other way around. Having the cons knowing the source has nothing to provide.

EDIT

Here is a very clear post on what iteratee are for. See the motivation parameter that talks about your problem... closing resources whenever what went well or wrong, but at the right time

Andy Petrella
  • 4,345
  • 26
  • 29
  • I'm not quite sure what you mean, particularly by "having the cons knowing the source has nothing to provide". My problem wasn't with closing when I'm done; it was with making sure that the file got closed even if I couldn't succcessfully set up the input chain. – Michael Ekstrand Jan 08 '12 at 00:15
  • Actually this iteratee, enumerator and enumeratee are there in order to resolve both (and other) cases. When the Source has finished producing (what you don't care of) and when the Consumer has finished (and it has finished if it fails actually). I'm gonna add a very helpful post, that will enlight you on that topic (which **is** a solution to your concern) – Andy Petrella Jan 08 '12 at 00:25
  • Thanks - that post does help a lot. It looks like an interesting solution, albeit one that assumes that everything is built around Iteratees. Doesn't look like it directly applies to using Java's standard filtered streams and readers, though - they know nothing of Iteratees. – Michael Ekstrand Jan 08 '12 at 01:03
  • I don't get you, because scalaz iteratees (referred in the post as IterV) are agnostic about the source, that's the point actually. And scalaz is doing things with regular InputStream (or even nio Channel) from the java, *by* using its implementation of iteratee. However, even if I'd be wrong (which has to be taken into account ^^). It wouldn't be that hard to create you own iteratee by having a producer of atomic data when needed execute the iteratee on 'em. – Andy Petrella Jan 08 '12 at 01:13
  • The iteratee solution works once you have an `InputStream`. However, that isn't my problem - I am concerned with safely building the `InputStream`, by connecting an `InputStream` with a `FilteredInputStream`, in the first place. Unless I am missing something, ScalaZ iteratees don't do anything to help with building a `FilteredInputStream` from an `InputStream` - they manage the data flow, whereas `FilteredInputStream` manages it itself. If `GZIPInputStream` were an iteratee, it would work. – Michael Ekstrand Jan 09 '12 at 15:30