SLC: public constructor for Using.Manager

Hi,

Using.Manager is a useful utility for resource management. Unfortunately, the only way to get hold of a Using.Manager is by calling Using.Manager.apply and passing a function to it that accepts a Using.Manager instance as a parameter. That function will then be run and any allocated resources will be cleaned up after it’s done. The resources’ lifecycle is thus inextricably linked to the control flow of the program.

This makes it impossible to use Using.Manager in a variety of use cases where you’re not in charge of the control flow. Many frameworks provide lifecycle hooks, such as Akka’s PostStop signal or ScalaTest’s afterEach/afterAll methods, or maybe you’re just trying to create a Future and clean up the resources allocated therein when it’s done.

How about we add a public API to create a Using.Manager and a close method to release the resources? Then we would be able to use it in these situations.

2 Likes

Nobody seems to be opposed, does that mean it’s a good idea?

I was hoping we would get more opinions here, but it doesn’t seem it’s a topic that people are currently interested in.

I am wondering myself if it’s not against the philosophy of using, where we on purpose make it unforgetable to close a resource. With this change I guess we will be able to actually forget running close. Isn’t that the case? Or am I missing the point?

1 Like

I didn’t comment before because I’m a bit fuzzy on the details, but my first impression was that it sounded counter-intuitive, as @tgodzik mentionned

For example, are there cases where you need to use Using/Using.Manager ?
My understanding is that is automates essentially closing things for you, but there are ways to close those things manually
So you could use these other manual ways with PostStop/afterEach/…, no ?

That is the point. Cats Effect Resource has a similar concept in its API.

This proposal sounds like it would give a similar capability to safely construct a resource with Using (and have things safely torn down in case of errors), then give back the result and a finalizer. This lets you integrate the safe resource construction with cleanup operations that are not lexically scoped (eg. the lifecycle hooks @mberndt mentions).

Yes, it is less safe, but it let’s users have some of the safety of the Using API when trying to interop with other APIs. Of course you never need to use Using.Manager, but the alternative is rolling more resource management code yourself, and adding extra ways to make errors.

It does more than just running close, it also ensures that earlier cleanup that throws an exception doesn’t prevent later cleanup from running, and that all errors during cleanup are collected (addSuppressed etc.). So there is useful functionality there that I’d like to access, even in cases where I don’t control the lifecycle. And I think the potential for misuse can be mitigated with suitable naming, like Manager.makeUnsafe or something.

No, of course not. But why force people to reinvent the wheel instead of just allowing them to use what’s already there in a context (lifecycle hooks) that was apparently not considered so far?

1 Like

Thanks for explaining, I agree that it makes sense. I think the PR itself should not be too large? Once it’s open I will put it on the core agenda.

1 Like

Per the Scaladoc:

There are multiple ways to automatically manage resources with Using.

For that reason, I am always “fuzzy” about Using, as commented above.

My use case is exclusively tests, where I only use Using.resource.

That is, integration with the framework is “throw”.

I am suspicious of complicating the simple lexical scoping (which I’m already fuzzy about). I’m also suspicious when a solution relies on helpful naming such as Unsafe.

However, I could imagine an API that takes an implicit framework that the manager delegates to, which by default is the status quo.

My comment is that I think it’s fine for a “standard library” mechanism to be limited in ambition because it’s trying to solve the casual case. An illustration might be concurrency support, where the library used to supply actors (for some use case) that were just “baby Akka”. Another illustration is collections, where there is an ongoing tension between the Scala library, Java collections, and custom implementations.

I sympathize with the sentiment that Using is a resource that ought to be re-used (instead of “reinventing the wheel”). However, I also think that re-use is given too much weight. Claude will reinvent that wheel for you! Just ask it for something round.