AsyncArrow?
The types:
type AsyncArrow<'a, 'b> = 'a -> Async<'b>
type AsyncFilter<'a, 'b, 'c, 'd> = AsyncArrow<'a, 'b> -> AsyncArrow<'c, 'd>
type AsyncFilter<'a, 'b> = AsyncArrow<'a, 'b> -> AsyncArrow<'a, 'b>
The async arrow type can have many interpretations and is particularly well suited at representing an async request-reply protocol. Data driven services are usually a composition of various request-reply interactions and it can be useful to reify the type.
The supported operations would be all those of arrows as seen here but also many others specific to Async and other types. Consider operations such as:
/// Invokes arrow g and then invokes f if successful (Choice1Of2).
val tryBefore (g:AsyncArrow<'a, Choice<'a, 'c>>) (f:AsyncArrow<'a, 'b>) : AsyncArrow<'a, Choice<'b, 'c>>
/// Creates an arrow which propagates its input into the output.
val strength (f:AsyncArrow<'a, 'b>) : AsyncArrow<'a, 'a * 'b>
/// Creates an arrow which filters inputs to the specified arrow.
val filterAsync (p:AsyncArrow<'a, bool>) (df:Async<'b>) (f:AsyncArrow<'a, 'b>) : AsyncArrow<'a, 'b>
Async filters represent mappings between arrows. A filter can be use to inject various cross-cutting concerns. For example, a timing filter:
let timing (log:NLog.Logger) : AsyncFilter<_,_,_,_> =
fun (f:AsyncArrow<'a, 'b>) a -> async {
let sw = System.Diagnostics.Stopwatch.StartNew()
let! res = f a
sw.Stop()
log.Trace("Time Elapsed={0}", sw.ElapsedMilliseconds)
return res
}
A filter can also change the input/output type of an arrow. This can be used for layering a JSON codec onto a request/reply protocol, for instance.
type AsyncSink<'a> = AsyncArrow<'a, unit>
A sink can be used to represent functions which are run solely for the side-effects. An example operation:
let mergeAllPar (ss:seq<AsyncSink<'a>>) : AsyncSink<'a> =
fun a -> ss |> Seq.map ((|>) a) |> Async.Parallel |> Async.Ignore
The paper Your Server as a Function explains the applications of these ideas to servers. The difference is mostly in the naming AsyncArrow is Service and AsyncFilter is Filter. Furthermore, they use Future abstraction which is similar to Async in terms of the operations it supports, but inherently different in that it is a promise (more like a TPL Task).