@@ -3,6 +3,22 @@ using System.Collections.Immutable;
namespace Just.Core.Collections ;
/// <summary>
/// Represents an immutable, ordered sequence of items with value‑ equality semantics.
/// </summary>
/// <typeparam name="T">The type of elements in the sequence.</typeparam>
/// <remarks>
/// <para>
/// This class is a thin wrapper around <see cref="ImmutableList{T}"/> that implements
/// <see cref="IReadOnlyList{T}"/>, <see cref="IEquatable{T}"/>, and value‑ based equality.
/// All modifications return new <see cref="ImmutableSequence{T}"/> instances, leaving the
/// original unchanged.
/// </para>
/// <para>
/// Subclasses may override <see cref="ConstructNew"/> to ensure mutation methods return
/// the correct derived type.
/// </para>
/// </remarks>
public class ImmutableSequence < T > :
IEnumerable < T > ,
IReadOnlyList < T > ,
@@ -11,17 +27,61 @@ public class ImmutableSequence<T> :
private static readonly Func < T ? , T ? , bool > CompareItem = EqualityComparer < T > . Default . Equals ;
private readonly ImmutableList < T > _values ;
/// <summary>
/// Initializes a new empty instance of the <see cref="ImmutableSequence{T}"/> class.
/// </summary>
public ImmutableSequence ( ) = > _values = [ ] ;
/// <summary>
/// Initializes a new instance of the <see cref="ImmutableSequence{T}"/> class that
/// wraps the specified <see cref="ImmutableList{T}"/>.
/// </summary>
/// <param name="values">The immutable list to wrap.</param>
public ImmutableSequence ( ImmutableList < T > values ) = > _values = values ;
/// <summary>
/// Initializes a new instance of the <see cref="ImmutableSequence{T}"/> class with
/// the elements from the provided enumerable sequence.
/// </summary>
/// <param name="values">The items to include in the sequence.</param>
public ImmutableSequence ( IEnumerable < T > values ) = > _values = [ . . values ] ;
/// <summary>
/// Initializes a new instance of the <see cref="ImmutableSequence{T}"/> class with
/// the elements from the provided read‑ only span.
/// </summary>
/// <param name="values">The items to include in the sequence.</param>
public ImmutableSequence ( ReadOnlySpan < T > values ) : this ( ImmutableList . Create ( values ) )
{
}
/// <summary>
/// Gets a value indicating whether the sequence contains any elements.
/// </summary>
public bool IsEmpty = > _values . IsEmpty ;
/// <summary>
/// Gets the number of elements in the sequence.
/// </summary>
public int Count = > _values . Count ;
/// <summary>
/// Gets the element at the specified zero‑ based index.
/// </summary>
/// <param name="index">The zero‑ based index of the element to get.</param>
/// <returns>The element at the specified index.</returns>
public T this [ int index ] = > _values [ index ] ;
/// <summary>
/// Gets the element at the specified position from the start or end of the sequence.
/// </summary>
/// <param name="index">An <see cref="Index"/> value (e.g., <c>^1</c> for the last element).</param>
/// <returns>The element at the specified position.</returns>
public T this [ Index index ] = > _values [ index ] ;
/// <summary>
/// Gets a new <see cref="ImmutableSequence{T}"/> containing the elements in the specified range.
/// </summary>
/// <param name="range">The range of elements to include.</param>
/// <returns>A new sequence representing the slice.</returns>
public ImmutableSequence < T > this [ Range range ]
{
get
@@ -31,25 +91,60 @@ public class ImmutableSequence<T> :
}
}
/// <summary>
/// Creates a new <see cref="ImmutableSequence{T}"/> from the provided immutable list.
/// Subclasses can override this to return instances of a more specific type.
/// </summary>
/// <param name="values">The immutable list that will become the internal storage.</param>
/// <returns>A new sequence containing the given items.</returns>
protected virtual ImmutableSequence < T > ConstructNew ( ImmutableList < T > values ) = > [ . . values ] ;
/// <summary>
/// Returns a new sequence with the specified value appended to the end.
/// </summary>
/// <param name="value">The value to add.</param>
/// <returns>A new sequence containing the original items followed by <paramref name="value"/>.</returns>
public ImmutableSequence < T > Add ( T value ) = > ConstructNew ( _values . Add ( value ) ) ;
/// <summary>
/// Returns a new sequence with the specified value inserted at the beginning.
/// </summary>
/// <param name="value">The value to add.</param>
/// <returns>A new sequence that starts with <paramref name="value"/> and then contains the original items.</returns>
public ImmutableSequence < T > AddFront ( T value ) = > ConstructNew ( _values . Insert ( 0 , value ) ) ;
/// <summary>
/// Returns an enumerator that iterates through the sequence.
/// </summary>
/// <returns>A <see cref="ImmutableList{T}.Enumerator"/> value type enumerator.</returns>
public ImmutableList < T > . Enumerator GetEnumerator ( ) = > _values . GetEnumerator ( ) ;
IEnumerator < T > IEnumerable < T > . GetEnumerator ( ) = > ( ( IEnumerable < T > ) _values ) . GetEnumerator ( ) ;
IEnumerator IEnumerable . GetEnumerator ( ) = > ( ( IEnumerable ) _values ) . GetEnumerator ( ) ;
public override string ToString ( ) = > string . Join ( Environment . NewLine , _values ) ;
/// <summary>
/// Determines whether this sequence is equal to another <see cref="ImmutableSequence{T}"/>.
/// Equality is based on the number of elements and the element‑ wise equality comparison
/// using the default equality comparer for <typeparamref name="T"/>.
/// </summary>
/// <param name="other">The sequence to compare with this instance. Can be <c>null</c>.</param>
/// <returns>
/// <c>true</c> if the sequences have the same length and all elements are equal;
/// <c>false</c> otherwise.
/// </returns>
public virtual bool Equals ( [ NotNullWhen ( true ) ] ImmutableSequence < T > ? other )
{
if ( other is null )
{
return false ;
}
if ( ReferenceEquals ( this , other ) )
{
return true ;
}
if ( _values . Count ! = other ? . _values . Count )
if ( _values . Count ! = other . _values . Count )
{
return false ;
}
@@ -65,7 +160,16 @@ public class ImmutableSequence<T> :
return true ;
}
/// <summary>
/// Determines whether the specified object is equal to the current sequence.
/// </summary>
/// <param name="obj">The object to compare with the current sequence.</param>
/// <returns><c>true</c> if <paramref name="obj"/> is an <see cref="ImmutableSequence{T}"/> and equals this instance; otherwise <c>false</c>.</returns>
public override bool Equals ( [ NotNullWhen ( true ) ] object? obj ) = > Equals ( obj as ImmutableSequence < T > ) ;
/// <summary>
/// Serves as a hash function for the sequence.
/// </summary>
/// <returns>A hash code that incorporates all elements in order.</returns>
public override int GetHashCode ( )
{
HashCode hash = new ( ) ;
@@ -78,6 +182,18 @@ public class ImmutableSequence<T> :
return hash . ToHashCode ( ) ;
}
/// <summary>
/// Determines whether two sequences are equal.
/// </summary>
/// <param name="left">The first sequence to compare.</param>
/// <param name="right">The second sequence to compare.</param>
/// <returns><c>true</c> if both sequences are <c>null</c> or they are considered equal; otherwise <c>false</c>.</returns>
public static bool operator = = ( ImmutableSequence < T > ? left , ImmutableSequence < T > ? right ) = > left is null ? right is null : left . Equals ( right ) ;
/// <summary>
/// Determines whether two sequences are not equal.
/// </summary>
/// <param name="left">The first sequence to compare.</param>
/// <param name="right">The second sequence to compare.</param>
/// <returns><c>true</c> if the sequences are not equal; otherwise <c>false</c>.</returns>
public static bool operator ! = ( ImmutableSequence < T > ? left , ImmutableSequence < T > ? right ) = > ! ( left = = right ) ;
}