Really appreciate such a deep and comprehensive reply. And I’m inlined with almost all of it. I’ll try to make a more refined statement to reason about:
There is nothing about lazy evaluation of anything in the library, as well as adopting some external abstraction in the first place (nor addressing performance aspects). It’s only DX. Initially I was addressing the problem of copy-pasting and refactoring the same code-approaches across our projects (e.g. time bounded synchronous expression evaluation or “singleton-resource” management). Stick with Elixir’s kernel (primary building blocks) as much as possible was a must. As a result I ended up poking around:
Stream.resource/3, which is (from the-day-one) a way to “handle resources” of the “stream-nature” (so it’s like 2-in-1: Elixir’s standard library resource management + acquired resource content streaming);foras a “composition facility” for enumerables (as I mentioned before, I started with some kind ofResource.use_all!/2, until I realised,foris a more natural candidate as it does exactly the same for lists as a syntactic sugar).
And all this “composable hustle” was just to refactor these patterns into separate library, so they target one concept at a time in a general way, so no preconditions nor assumptions on the nature of resources was made (should it be a file, database connection pool or whatever).
And that is where our points of view diverge:
- you can view
Stream.resource/3as a stream in the first place, but with “resource management” baked in; - being able to:
but do-it-by-yourself for resources of “non-stream-nature” every time you need it (no matter how: usingStream.resource( fn -> File.open!("sample") end, fn file -> case IO.read(file, :line) do data when is_binary(data) -> {[data], file} _ -> {:halt, file} end end, fn file -> File.close(file) end )at_resource-approach or adoptingStream.resource/3); - you can view
foras an exclusive facility for enumerables; - and at the same time being able to:
for lists, but:for a <- as, b <- bs, a < b, c <- cs, a + b + c < 10, do: a + b + c
for streams, althoughStream.product(as, bs) |> Stream.filter(fn {a, b} -> a < b end) |> Stream.product(c) |> Stream.fitler(fn {{a, b}, c} -> a + b + c < 10 end) |> Stream.map(fn {{a, b}, c} -> a + b + c end)foralso accepts streams (when you really need aStreamas a result).
So these libraries are nothing more than to view these points as a some sort of asymmetry:
- being able to use Elixir’s kernel “resource management” facility (
Stream.resource/3) without “stream-nature” assumption on the acquired resource content (which could be implemented usingat_resource-approach); - being able to use
foras a for-comprehension (just as its name suggests
, I’m totally ok with Kernel.SpecialForms.for/1).






















