IO improvements
Pre-releaseawaitAny improved
The awaitAny function - that works with any MonadUnliftIO trait-implementing type (IO, Eff, etc.), accepts a number of asynchronous computations, and returns when the first computation completes (a functional version of Task.WhenAny) - will now cancel all computations once the first one has completed. This is probably the most desirable default functionality, rather than letting all computations continue until they're complete.
This example will only write out "C", the A and B processes will be cancelled once the C process completes:
var eff = awaitAny(delayed("A", 9),
delayed("B", 7),
delayed("C", 5));
ignore(eff.Run(env));
Console.ReadKey();
static Eff<Unit> delayed(string info, int time) =>
Eff.lift(async e =>
{
await Task.Delay(time * 1000, e.Token);
Console.WriteLine(info);
return unit;
});UninterruptibleIO
The MonadUnliftIO trait now has a new method: UninterruptibleIO:
public static virtual K<M, A> UninterruptibleIO<A>(K<M, A> ma) =>
M.MapIO(ma, io => io.Uninterruptible());The default behaviour is to block cancellation-requests that are raised via the EnvIO cancellation-token. It does this by creating a new local-EnvIO context that ignores the parent's cancellation context.
There's an UninterruptibleIO extension method and an uninterruptible function in the Prelude that will work with any MonadUnliftIO trait-implementing type (IO, Eff, etc.).
If you wished to go back to the old awaitAny behaviour then you can wrap each computation with uninterruptible(...). If we do that with the previous example:
var eff = awaitAny(uninterruptible(delayed("A", 9)),
uninterruptible(delayed("B", 7)),
uninterruptible(delayed("C", 5)));Then the output will be:
C
B
AWhich was the previous default functionality.
Timeout improvements
- Added timeout support to
EnvIO- aTimeSpancan be provided for whenCancellationTokenSourceisnulland it will then use that timeout value when constructing a new one. - There's also
EnvIO.LocalWithTimeout(TimeSpan)andEnvIO.LocalCancelWithTimeout(TimeSpan), which are the most useful functions as they allow for a local context with a timeout attached. - The
IO<A>.Timeout,IO.timeout, andPrelude.timeoutfunctions have been improved to leverage this new functionality. The previous implementation was quite naive and not the most efficient, so this needed doing.
Effects prelude
I've moved the more general (K<M, A>) based IO operators/functions to the root of the Effects folder to show they're general and not just for the IO type. It makes functions slightly more discoverable in the API documentation.