LanguageExt.Core

LanguageExt.Core Immutable Collections IteratorAsync

IteratorAsync<A> is a functional-wrapper for IAsyncEnumerator<A>. The abstraction leaks a little, so it's worth understanding how it works by reading the details below. On the whole it behaves like an immutable stream that caches values as it goes, but there's some footguns that you should be aware of so that they can be avoided.

Problem: IAsyncEnumerator<A>

Nobody in their right mind would invent an interface like IAsyncEnumerator<A> today.

Solution: IteratorAsync<A>

IteratorAsync<A> still uses IAsyncEnumerator<A> internally, but it makes it thread-safe and functional. From the outside the type acts and works exactly like any other immutable sequence, but internally it does some quite complex processing to achieve this with an IAsyncEnumerator<A> reference.

You may say "Why not just drop IAsyncEnumerator<A>?" - which is a completely valid position to hold. Unfortunately, IAsyncEnumerable and IAsyncEnumerator are baked into the CPS state-machine that is used for yield return and yield break. So, we don't get to ignore those types, and instead we need to make them play nice.

IAsyncEnumerable<A> has a method called GetAsyncEnumerator() which is used to access an IAsyncEnumerator<A>. A new extension method is available called GetIteratorAsync(), this will yield an IteratorAsync<A>.

Contents

Sub modules

Extensions
Trait

class IteratorAsync <A> Source #

Wrapper for IEnumerator that makes it work like an immutable sequence.

It is thread-safe and impossible for any item in the sequence to be enumerated more than once.

IEnumerator from the .NET BCL has several problems:

  • It's very imperative
  • It's not thread-safe, two enumerators can't be shared

The lack of support for sharing of enumerators means that it's problematic using it internally in types like StreamT, or anything that needs to keep an IEnumerator alive for any period of time.

NOTE: There is a per-item allocation to hold the state of the iterator. These are discarded as you enumerate the sequence. However, technically it is possible to hold the initial Iterator value and subsequently gain a cached sequence of every item encountered in the enumerator.

That may well be valuable for circumstances where re-evaluation would be expensive. However, for infinite-streams this would be extremely problematic. So, make sure you discard any previous IteratorAsync values as you walk the sequence.

Parameters

type A

Item value type

Fields

field IteratorAsync<A> Empty = new Nil() Source #

Empty iterator

Properties

property ValueTask<A> Head Source #

Head element

property ValueTask<IteratorAsync<A>> Tail Source #

Tail of the sequence

property ValueTask<bool> IsEmpty Source #

Return true if there are no elements in the sequence.

property ValueTask<long> Count Source #

Return the number of items in the sequence.

Requires all items to be evaluated, this will happen only once however.

Methods

method IteratorAsync<A> Clone () Source #

Clone the iterator so that we can consume it without having the head item referenced. This will stop any GC pressure when processing large or infinite sequences.

method IteratorAsync<A> Split () Source #

When iterating a sequence, it is possible (before evaluation of the Tail) to Terminate the current iterator and to take a new iterator that continues on from the current location. The reasons for doing this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that can't be garbage collected.

Any other iterator references that came before this one will terminate at this point. Splitting the previous and subsequent iterators here.

Parameters

returns

New iterator that starts from the current iterator position.

method IAsyncEnumerable<A> AsEnumerable ([EnumeratorCancellation] CancellationToken token) Source #

Create an IEnumerable from an Iterator

method IteratorAsync<B> Select <B> (Func<A, B> f) Source #

Functor map

method IteratorAsync<B> Map <B> (Func<A, B> f) Source #

Functor map

method IteratorAsync<B> Bind <B> (Func<A, IteratorAsync<B>> f) Source #

Monad bind

method IteratorAsync<C> SelectMany <B, C> (Func<A, IteratorAsync<B>> bind, Func<A, B, C> project) Source #

Monad bind

method IteratorAsync<B> Apply <B> (IteratorAsync<Func<A, B>> ff, IteratorAsync<A> fa) Source #

Applicative apply

method IteratorAsync<A> Concat (IteratorAsync<A> other) Source #

Concatenate two iterators

method ValueTask<S> Fold <S> ( S state, Func<A, Func<S, S>> f) Source #

Fold the sequence while there are more items remaining

method ValueTask<S> Fold <S> ( S state, Func<S, A, S> f) Source #

Fold the sequence while there are more items remaining

method ValueTask<S> FoldWhile <S> ( S state, Func<A, Func<S, S>> f, Func<(S State, A Value), bool> predicate) Source #

Fold the sequence while the predicate returns true and there are more items remaining

method ValueTask<S> FoldWhile <S> ( S state, Func<S, A, S> f, Func<(S State, A Value), bool> predicate) Source #

Fold the sequence while the predicate returns true and there are more items remaining

method ValueTask<S> FoldUntil <S> ( S state, Func<A, Func<S, S>> f, Func<(S State, A Value), bool> predicate) Source #

Fold the sequence until the predicate returns true or the sequence ends

method ValueTask<S> FoldUntil <S> ( S state, Func<S, A, S> f, Func<(S State, A Value), bool> predicate) Source #

Fold the sequence until the predicate returns true or the sequence ends

method IteratorAsync<A> Merge (IteratorAsync<A> other) Source #

Interleave two iterator sequences together

Whilst there are items in both sequences, each is yielded after the other. Once one sequence runs out of items, the remaining items of the other sequence is yielded alone.

method IteratorAsync<(A First , A Second)> Zip (IteratorAsync<A> other) Source #

Zips the items of two sequences together

The output sequence will be as long as the shortest input sequence.

method ValueTask DisposeAsync () Source #

Dispose

method IAsyncEnumerator<A> GetAsyncEnumerator (CancellationToken token) Source #

Get enumerator

Parameters

returns

method string ToString () Source #

Operators

operator + (IteratorAsync<A> ma, IteratorAsync<A> mb) Source #

Combine two sequences

class Nil Source #

Nil iterator case

The end of the sequence.

Fields

field IteratorAsync<A> Default = new Nil() Source #

Properties

property ValueTask<A> Head Source #

Head element

property ValueTask<IteratorAsync<A>> Tail Source #

Tail of the sequence

property ValueTask<bool> IsEmpty Source #

Return true if there are no elements in the sequence.

property ValueTask<long> Count Source #

Return the number of items in the sequence.

Requires all items to be evaluated, this will happen only once however.

Methods

method IteratorAsync<A> Clone () Source #

Clone the iterator so that we can consume it without having the head item referenced. This will stop any GC pressure.

method IteratorAsync<A> Split () Source #

When iterating a sequence, it is possible (before evaluation of the Tail) to Terminate the current iterator and to take a new iterator that continues on from the current location. The reasons for doing this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that can't be garbage collected.

Parameters

returns

New iterator that starts from the current iterator position

method ValueTask DisposeAsync () Source #

class Cons Source #

Cons iterator case.

Contains a head value and a tail that represents the rest of the sequence.

Methods

method void Deconstruct (out ValueTask<A> head, out ValueTask<IteratorAsync<A>> tail) Source #

class IteratorAsync Source #

Methods

method IteratorAsync<A> from <A> (IAsyncEnumerable<A> enumerable) Source #

Create an iterator from an IAsyncEnumerable

Parameters

type A
param enumerable
returns

method IteratorAsync<A> singleton <A> (A head) Source #

Construct a singleton sequence

Parameters

type A

Bound value type

param head

Head item

returns

IteratorAsync

method IteratorAsync<A> Cons <A> (A head, IteratorAsync<A> tail) Source #

Construct a sequence from a head item and a tail sequence

Parameters

type A

Bound value type

param head

Head item

param tail

Tail sequences

returns

IteratorAsync

method IteratorAsync<A> Cons <A> (A head, Func<IteratorAsync<A>> tail) Source #

Construct a sequence from a head item and a tail sequence

Parameters

type A

Bound value type

param head

Head item

param tail

Tail sequences

returns

IteratorAsync

method IteratorAsync<A> Nil <A> () Source #

Empty sequence

Parameters

type A

Bound value type

returns

IteratorAsync