-
Notifications
You must be signed in to change notification settings - Fork 11
Description
While most of the above have trivial semantics and should sometimes take an Async overload (i.e., findIndexAsync and initAsync for the lambda), I'm not entirely sure of the usefulness of TaskSeq.cache (and we already have TaskSeq.toSeqCached), but perhaps for parity it should be included.
A note on TaskSeq.cast vs Seq.cast
While the docs on Seq.cast say that it is meant for a "loosely typed sequence", to be cast to a generically strongly typed sequence, it can be used just fine to cast an F# seq, because it also implements IEnumerable (i.e., the non-generic variant).
// this is fine
let x: seq<uint> = seq { 1 } |> Seq.castBut F# also allows you to perform an illegal cast:
// this won't give any warnings, but will throw an exception
let x: seq<Guid> = seq { 1 } |> Seq.castSince IAsyncEnumerable<'T> only exists as a typed sequence, we should honor that and only allow valid casts. However, F# doesn't allow a constraint like 'T :> 'U, the rh-side must be a concrete type. This means that in the end, it'll effectively work the same way as for Seq.cast through boxing. Alternatively, if the type-relation is known ahead of time, users should probably just write TaskSeq.map (fun x -> x :> _) just as they would for seq<_>.
// this is fine
let x: seq<uint> = taskSeq { 1 } |> TaskSeq.cast// this *should* raise a compile-time exception, but there's no way to enforce that
let x: seq<Guid> = taskSeq { 1 } |> TaskSeq.cast // incompatibleFor comparison, Linq.Enumerable.Cast works through the untyped Enumerable as well.
EDIT: to get better parity with Seq.cast and Linq.Enumerable.Cast, I decided to only allow it to work with untyped task sequences (that is: IAsyncEnumerable<obj>). I've updated the signature below. In addition, we'll be adding a box and unbox helper as well, the latter only for value types. If users want a reinterpret_cast style cast, they can use TaskSeq.box >> TaskSeq.cast. Due to the static way this is compiled, this won't add overhead.
TODO list:
-
length, see Increase surface area: TaskSeq.length, lengthBy and lengthByAsync #53 -
lengthBy, see Increase surface area: TaskSeq.length, lengthBy and lengthByAsync #53 -
lengthByAsync, see Increase surface area: TaskSeq.length, lengthBy and lengthByAsync #53 -
allPairs// later, requirescacheto be implemented -
indexed, see AddTaskSeq.indexed,findIndex,tryFindIndex,findIndexAsync,tryFindIndexAsync#68 -
cache// later, see MBP approach of AsyncSeq -
cast, see Add more surface area functions:TaskSeq.cast,boxandunbox#67 -
box, see Add more surface area functions:TaskSeq.cast,boxandunbox#67 -
unbox, see Add more surface area functions:TaskSeq.cast,boxandunbox#67 -
concat, see ImplementTaskSeq.init,initAsync,initInfinite,initInfiniteAsyncandTaskSeq.concat#69 -
findIndex, see AddTaskSeq.indexed,findIndex,tryFindIndex,findIndexAsync,tryFindIndexAsync#68 -
findIndexAsync, see AddTaskSeq.indexed,findIndex,tryFindIndex,findIndexAsync,tryFindIndexAsync#68 -
tryFindIndex, see AddTaskSeq.indexed,findIndex,tryFindIndex,findIndexAsync,tryFindIndexAsync#68 -
tryFindIndexAsync, see AddTaskSeq.indexed,findIndex,tryFindIndex,findIndexAsync,tryFindIndexAsync#68 -
contains -
exists -
existsAsync -
init, see ImplementTaskSeq.init,initAsync,initInfinite,initInfiniteAsyncandTaskSeq.concat#69 -
initAsync, see ImplementTaskSeq.init,initAsync,initInfinite,initInfiniteAsyncandTaskSeq.concat#69 -
initInifinite, see ImplementTaskSeq.init,initAsync,initInfinite,initInfiniteAsyncandTaskSeq.concat#69 -
initInifiniteAsync, see ImplementTaskSeq.init,initAsync,initInfinite,initInfiniteAsyncandTaskSeq.concat#69
Proposals of signatures:
module TaskSeq =
val length: source: taskSeq<'T> -> Task<int>
val lengthBy: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<int>
val lengthByAsync: predicate: ('T -> #Task<bool>) -> source: taskSeq<'T> -> Task<int>
val allPairs: source1: taskSeq<'T> -> source2: taskSeq<'U> -> taskSeq<'T * 'U>
val indexed: source: taskSeq<'T> -> taskSeq<int * 'T>
val cache: source: taskSeq<'T> -> taskSeq<'T> // later, see MBP approach of AsyncSeq
val cast: source: taskSeq<obj> -> taskSeq<'U>
val box: source: taskSeq<'T> -> taskSeq<obj>
val unbox<'T when 'T: struct> : source: taskSeq<obj> -> taskSeq<'U>
val concat: source1: taskSeq<'T> -> source2: taskSeq<'T> -> taskSeq<'T>
val findIndex: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<int>
val findIndexAsync: predicate: ('T -> Task<bool>) -> source: taskSeq<'T> -> Task<int>
val tryFindIndex: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<int option>
val tryFindIndexAsync: predicate: ('T -> Task<bool>) -> source: taskSeq<'T> -> Task<int option>
val contains: value: 'T -> source: taskSeq<'T> -> Task<bool> (requires comparison)
val exists: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task<bool>
val existsAsync: predicate: ('T -> Task<bool>) -> source: taskSeq<'T> -> Task<bool>
val init: count: int -> initializer: (int -> 'T) -> taskSeq<'T>
val initAsync: count: int -> initializer: (int -> Task<'T>) -> taskSeq<'T>
val initInfinite: initializer: (int -> 'T) -> taskSeq<'T>
val initInfiniteAsync: initializer: (int -> Task<'T>) -> taskSeq<'T>