Elixir’s kernel has Stream.resource/3 function, that acts “resource-like” (it defines resource acquiring, usage and releasing phases). As a “resource-like” facility it also guarantees resource releasing once it was acquired (no matter what usage phase returns or even raises). However, it biased toward “stream-like” resources (as the name of the module suggests). So every time I want to safely handle a resource in my code, I found myself adopting Stream.resource/3
for “singleton-resource”.
E.g. in case of WithTimeout (also got a post about it) local task supervisor is spawned under the hood, so we need to ensure it’s properly released (no matter what happens in between). And adopting Stream.resource/3
for non-stream-like-resource could be quite cumbersome.
So I also decided to extract this repeatable pattern into separate library.
While doing so I’ve came across a noticeable use case — handling several resources. Exposing some kind of @spec use_all!(list(resource(a)), (list(a) -> b)) :: b when a: var, b: var
felt unnatural. On the other hand, when you found yourself “flat-mapping” your data types (Resource.use!(r1, fn a -> Resource.use(r2, fn b -> ... end) end)
), it is the monadic glimpse whispering you. So long story short, I’ve equipped the library with for-comprehension batteries to be able to compose resources (I exported general purpose for-comprehension from Bindable).
As a disclaimer: there is no reasonable filtering semantics for Resource
(so there is no Bindable.Empty
implementation for Resource
, that is why if
is not available for Resource
inside for-comprehension).
At the end of the day, the library exposes composable facility for safe resource handling.