Would you agree with this viewpoint, and do you think cats-effect should retain its philosophy?
Strongly agree with that viewpoint. This is basically how I think about it as well.
As for whether or not cats-effect
IO should retain that mode of operation… I'm honestly not sure. I kind of like the fact that it fills the niche at the "throughput by default" end of the spectrum, since the rest of the spectrum is well covered by the other options. However, the lack of auto-yielding has bitten me (though very, very very rarely for the reasons that @SystemFw pointed out: most code is fairer than you think it is).
I think there's room for discussion on this point for sure.
Question: given a factory method that effectfully creates an object in, say,
F, and the created object is also created from a class parameterized over an effect type, do I need to do something like:
def create( )(...): F[Thing[G]]
or do you all normally just leave everything as
F and be done with it assuming all effect types converge to
I share that opinion. But, I'm wondering if there are cases where you have a lot of flatmapped actions stacked up on each other in a gigantic IO program, and you know you would like to yield every once in a while, but it's hard to inject that into your code because it's not really clear where those points actually are.
That's the concern exactly. In practice I've found this is rare to the point of non-existent, but I imagine it could happen. The only times I've been bitten by lack of fairness, it was my own fault and relatively easy to fix. If you think about what has to happen for a long series of CPU-hogging code to avoid any async boundaries at all, it usually requires a ton of pure compute code (so, you're doing
map with some
f which is expensive but pure). In that case, it doesn't matter whether your semantics are auto-yielding or not: it's going to hog the thread.
For there to be code which can be more fairness-optimized by some mechanism in the effect type, but isn't already so optimized, you need a ton of
delay actions bound together with
flatMaps. A ton of them. Without any
async in between and without any
shifts to other pools. That… happens… very very very rarely.
Which is to say that auto-yielding isn't as helpful as it sounds in practice. Certainly still meaningful, but more meaningful on paper than in reality.
If I use Async.async() inside of Resource.use(), I need to ensure myself that somehow the async operation is completed prior to the resource's release method is called
use method will not be called until (at least!) the callback inside of
async is run. Which is to say, you have control over it. Remember that
async doesn't mean "parallel", it just means non-blocking.
R. Within the
usemethod I use Async.async() on
R(so the callback necessarily has to be called after the
usemethod). Assuming I am not altering ExecutionContexts or ContextShifts, is it possible for the
releasemethod to be called prior to the callback finishing if I do not explicitly synchronize this?
use, not after
start, then you have a problem
async, you will be safe
asyncyou see it