Std

Mainly a port of Rust's std.
git clone https://git.philomathiclife.com/repos/Std
Log | Files | Refs | README

Result.cs (18865B)


      1 using Std.Clone;
      2 using Std.Cmp;
      3 using static Std.Cmp.Ordering;
      4 using Std.Convert;
      5 using Std.Hashing;
      6 using Std.Iter;
      7 using Std.Maybe;
      8 using static Std.Maybe.Maybe<Std.Cmp.Ordering>;
      9 using Std.Ops;
     10 using System;
     11 using System.Runtime.CompilerServices;
     12 using System.Runtime.InteropServices;
     13 #region Namespaces
     14 namespace Std.Result {
     15     #region Types
     16     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)]
     17     public struct IntoIterator<T>: IDoubleEndedIterator<T>, IExactSizeIterator<T>, IFusedIterator<T>, IInto<IntoIterator<T>>, IIntoIterator<T, IntoIterator<T>> where T: notnull {
     18 
     19         #region Type-level Constructors
     20         #endregion
     21 
     22         #region Instance Constructors
     23         public IntoIterator() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");
     24         internal IntoIterator(Maybe<T> maybe) => _maybe = maybe;
     25         #endregion
     26 
     27         #region Type-level Fields
     28         #endregion
     29 
     30         #region Instance Fields
     31         Maybe<T> _maybe;
     32         #endregion
     33 
     34         #region Type-level Properties
     35         #endregion
     36 
     37         #region Instance Properties
     38         #endregion
     39 
     40         #region Type-level Functions
     41         #endregion
     42 
     43         #region Instance Functions
     44         public Result<Unit, ulong> AdvanceBackBy(ulong n) => IDoubleEndedIterator<T>.AdvanceBackByDefault(ref this, n);
     45         public Result<Unit, ulong> AdvanceBy(ulong n) => IIterator<T>.AdvanceByDefault(ref this, n);
     46         public readonly ulong Count() => _maybe.IsSome ? 1ul : ulong.MinValue;
     47         public TInit Fold<TInit>(TInit init, Fn<TInit, T, TInit> f) where TInit: notnull => IIterator<T>.FoldDefault(ref this, init, f);
     48         public override readonly bool Equals(object? _) => false;
     49         public override readonly int GetHashCode() => 0;
     50         public readonly IntoIterator<T> Into() => this;
     51         public readonly IntoIterator<T> IntoIter() => this;
     52         public Maybe<T> Last() => IIterator<T>.LastDefault(ref this);
     53         public Maybe<T> Next() {
     54 
     55             var val = _maybe;
     56             _maybe = Maybe<T>.None();
     57             return val;
     58         }
     59         public Maybe<T> NextBack() => Next();
     60         public TInit RFold<TInit>(TInit init, Fn<TInit, T, TInit> f) where TInit: notnull => IDoubleEndedIterator<T>.RFoldDefault(ref this, init, f);
     61         public readonly Prod<ulong, Maybe<ulong>> SizeHint() => _maybe.IsSome ? new(1ul, new(1ul)) : new(ulong.MinValue, new(ulong.MinValue));
     62         public override readonly string ToString() => $"IntoIterator<{typeof(T).Name}>";
     63         public Result<TInit, TErr> TryFold<TInit, TErr>(TInit init, Fn<TInit, T, Result<TInit, TErr>> f) where TInit: notnull where TErr: notnull => IIterator<T>.TryFoldDefault(ref this, init, f);
     64         public Result<TInit, TErr> TryRFold<TInit, TErr>(TInit init, Fn<TInit, T, Result<TInit, TErr>> f) where TInit: notnull where TErr: notnull => IDoubleEndedIterator<T>.TryRFoldDefault(ref this, init, f);
     65         readonly Result<IntoIterator<T>, Bottom> ITryInto<IntoIterator<T>, Bottom>.TryInto() => new(this);
     66         #endregion
     67 
     68         #region Operators
     69         #endregion
     70 
     71         #region Types
     72         #endregion
     73     }
     74     // Even though TOK and TErr can be non-readonly structs, we can still make Result readonly without any performance hit so long
     75     // as we never access any members of _ok or _err.
     76     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)]
     77     public readonly struct Result<TOK, TErr>: ISum<TOK, TErr>, IInto<Result<TOK, TErr>>, IIntoIterator<TOK, IntoIterator<TOK>> where TOK: notnull where TErr: notnull {
     78 
     79         #region Type-level Constructors
     80         #endregion
     81 
     82         #region Instance Constructors
     83         public Result() => throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");
     84         [MethodImpl(MethodImplOptions.AggressiveInlining)]
     85         public Result(TErr err) => (Var, _ok, _err) = (Tag.Err, default!, err);
     86         [MethodImpl(MethodImplOptions.AggressiveInlining)]
     87         public Result(TOK ok) => (Var, _ok, _err) = (Tag.OK, ok, default!);
     88         #endregion
     89 
     90         #region Type-level Fields
     91         #endregion
     92 
     93         #region Instance Fields
     94         // This is the "standard library"; so it is important that code is quick.
     95         // Due to the pervasive usage of Results, we decide to expose these fields to the rest of the shared library.
     96         // This allows code to avoid overhead from calling the monadic API or even functions like Unwrap.
     97         // It is imperative that code manually verify IsOK or IsErr is true first though.
     98         // KEEP THIS ORDER! It is very common for TErr to be one byte in size. By having it located after _ok,
     99         // we increase the probability of having a more compact Result.
    100         internal readonly TOK _ok;
    101         internal readonly TErr _err;
    102         public readonly Tag Var;
    103         #endregion
    104 
    105         #region Type-level Properties
    106         #endregion
    107 
    108         #region Instance Properties
    109         public readonly bool IsErr => Var == Tag.Err;
    110         public readonly bool IsOK => Var == Tag.OK;
    111         public readonly Var2 Variant => Var == Tag.OK ? Var2.V0 : Var2.V1;
    112         public readonly TOK Variant0 => Var == Tag.OK ? _ok : throw new InvalidOperationException($"The Result variant, {Var.ToString()} is not OK!");
    113         public readonly TErr Variant1 => Var == Tag.Err ? _err : throw new InvalidOperationException($"The Result variant, {Var.ToString()} is not Err!");
    114         #endregion
    115 
    116         #region Type-level Functions
    117         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    118         public static Result<TOK, TErr> Err(TErr val) => new(val);
    119         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    120         public static Result<TOK, TErr> OK(TOK val) => new(val);
    121         #endregion
    122 
    123         #region Instance Functions
    124         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    125         public readonly Result<T, TErr> And<T>(Result<T, TErr> val) where T: notnull => IsOK ? val : new(_err);
    126         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    127         public readonly Result<T, TErr> AndThen<T>(Fn<TOK, Result<T, TErr>> f) where T: notnull => IsOK ? f(_ok) : new(_err);
    128         public override readonly bool Equals(object? _) => false;
    129         public readonly Maybe<TErr> Err() => IsOK ? Maybe<TErr>.None() : new(_err);
    130         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    131         public readonly TOK Expect(string msg) => IsOK ? _ok : throw new InvalidOperationException($"{msg}: {_err.ToString()}");
    132         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    133         public readonly TErr ExpectErr(string msg) => !IsOK ? _err : throw new InvalidOperationException($"{msg}: {_ok.ToString()}");
    134         public override readonly int GetHashCode() => 0;
    135         public readonly Result<TOK, TErr> Into() => this;
    136         public readonly IntoIterator<TOK> IntoIter() => new(OK());
    137         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    138         public readonly Result<T, TErr> Map<T>(Fn<TOK, T> f) where T: notnull => IsOK ? new(f(_ok)) : new(_err);
    139         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    140         public readonly Result<TOK, T> MapErr<T>(Fn<TErr, T> f) where T: notnull => IsOK ? new(_ok) : new(f(_err));
    141         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    142         public readonly T MapOr<T>(T Default, Fn<TOK, T> f) where T: notnull => IsOK ? f(_ok) : Default;
    143         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    144         public readonly T MapOrElse<T>(Fn<TErr, T> Default, Fn<TOK, T> f) where T: notnull => IsOK ? f(_ok) : Default(_err);
    145         public readonly Maybe<TOK> OK() => IsOK ? new(_ok) : Maybe<TOK>.None();
    146         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    147         public readonly Result<TOK, T> Or<T>(Result<TOK, T> val) where T: notnull => IsOK ? new(_ok) : val;
    148         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    149         public readonly Result<TOK, T> OrElse<T>(Fn<TErr, Result<TOK, T>> f) where T: notnull => IsOK ? new(_ok) : f(_err);
    150         public override readonly string ToString() => IsOK ? $"OK({_ok.ToString()})" : $"Err({_err.ToString()})";
    151         readonly Result<Result<TOK, TErr>, Bottom> ITryInto<Result<TOK, TErr>, Bottom>.TryInto() => new(this);
    152         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    153         public readonly TOK Unwrap() => IsOK ? _ok : throw new InvalidOperationException($"Err({_err.ToString()})");
    154         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    155         public readonly TErr UnwrapErr() => !IsOK ? _err : throw new InvalidOperationException($"OK({_ok.ToString()})");
    156         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    157         public readonly TOK UnwrapOr(TOK Default) => IsOK ? _ok : Default;
    158         [MethodImpl(MethodImplOptions.AggressiveInlining)]
    159         public readonly TOK UnwrapOrElse(Fn<TErr, TOK> f) => IsOK ? _ok : f(_err);
    160         #endregion
    161 
    162         #region Operators
    163         #endregion
    164 
    165         #region Types
    166         public enum Tag: byte {
    167             Err = byte.MinValue,
    168             OK = byte.MaxValue,
    169         }
    170         #endregion
    171     }
    172     public static class Functions {
    173 
    174         #region Type-level Constructors
    175         #endregion
    176 
    177         #region Instance Constructors
    178         #endregion
    179 
    180         #region Type-level Fields
    181         #endregion
    182 
    183         #region Instance Fields
    184         #endregion
    185 
    186         #region Type-level Properties
    187         #endregion
    188 
    189         #region Instance Properties
    190         #endregion
    191 
    192         #region Type-level Functions
    193         public static Result<TOK, TErr> Clamp<TOK, TErr>(this Result<TOK, TErr> self, Result<TOK, TErr> min, Result<TOK, TErr> max) where TOK: notnull, IOrd<TOK> where TErr: notnull, IOrd<TErr> {
    194 
    195             System.Diagnostics.Trace.Assert(min.Cmp(in max).Var != Ord.Greater);
    196             return self.Cmp(in min).Var switch {
    197                 Ord.Less => min,
    198                 Ord.Equivalent => self,
    199                 _ => self.Cmp(in max).Var == Ord.Greater ? max : self,
    200             };
    201         }
    202         public static Result<TOK, TErr> Clone<TOK, TErr>(in this Result<TOK, TErr> self) where TOK: notnull, IClone<TOK> where TErr: notnull, IClone<TErr> => self.IsOK ? new(self._ok.Clone()) : new(self._err.Clone());
    203         public static Ordering Cmp<TOK, TErr>(in this Result<TOK, TErr> self, in Result<TOK, TErr> other) where TOK: notnull, IOrd<TOK> where TErr: notnull, IOrd<TErr> => (self.IsOK, other.IsOK) switch {
    204             (true, true) => self._ok.Cmp(in other._ok),
    205             (true, false) => Greater,
    206             (false, true) => Less,
    207             _ => self._err.Cmp(in other._err),
    208         };
    209         public static int CompareToComparable<TOK, TErr>(in this Result<TOK, TErr> self, in Result<TOK, TErr> other) where TOK: notnull, IComparable<TOK> where TErr: notnull, IComparable<TErr> => (self.IsOK, other.IsOK) switch {
    210             (true, true) => self._ok.CompareTo(other._ok),
    211             (true, false) => 1,
    212             (false, true) => -1,
    213             _ => self._err.CompareTo(other._err),
    214         };
    215         public static bool Contains<TOK, TErr, T>(in this Result<TOK, TErr> self, in T val) where TErr: notnull where TOK: notnull, IPartialEq<TOK, T> where T: notnull, IPartialEq<T, TOK> => self.IsOK && self._ok == val;
    216         public static bool ContainsErr<TOK, TErr, T>(in this Result<TOK, TErr> self, in T val) where TOK: notnull where TErr: notnull, IPartialEq<TErr, T> where T: notnull, IPartialEq<T, TErr> => self.IsErr && self._err == val;
    217         public static bool Eq<TOK, TErr, TOK2, TErr2>(in this Result<TOK, TErr> self, in Result<TOK2, TErr2> other) where TOK: notnull, IPartialEq<TOK, TOK2> where TErr: notnull, IPartialEq<TErr, TErr2> where TOK2: notnull, IPartialEq<TOK2, TOK> where TErr2: notnull, IPartialEq<TErr2, TErr> => (self.IsOK, other.IsOK) switch {
    218             (true, true) => self._ok == other._ok,
    219             (false, false) => self._err == other._err,
    220             _ => false,
    221         };
    222         public static bool EqualsEquality<TOK, TErr>(in this Result<TOK, TErr> self, in Result<TOK, TErr> other) where TOK: notnull, IEquality<TOK> where TErr: notnull, IEquality<TErr> => (self.IsOK, other.IsOK) switch {
    223             (true, true) => self._ok.Equals(in other._ok),
    224             (false, false) => self._err.Equals(in other._err),
    225             _ => false,
    226         };
    227         public static bool EqualsEquatable<TOK, TErr>(in this Result<TOK, TErr> self, in Result<TOK, TErr> other) where TOK: notnull, IEquatable<TOK> where TErr: notnull, IEquatable<TErr> => (self.IsOK, other.IsOK) switch {
    228             (true, true) => self._ok.Equals(other._ok),
    229             (false, false) => self._err.Equals(other._err),
    230             _ => false,
    231         };
    232         public static Result<TOK, TErr> Flatten<TOK, TErr>(this Result<Result<TOK, TErr>, TErr> self) where TOK: notnull where TErr: notnull => self.AndThen(Convert.Functions.Identity);
    233         public static Result<TOK, TErr> FromIter<TOK, TErr, T, TIter>(TIter iter) where T: notnull where TIter: notnull, IIterator<Result<T, TErr>> where TErr: notnull where TOK: notnull, IFromIterator<TOK, T> {
    234 
    235             var mbe = Maybe<TErr>.None();
    236             var v = TOK.FromIter(iter.Scan(new Unit(), (ref Unit _, ref Result<T, TErr> item) => {
    237                 
    238                 if (item.IsErr) {
    239                     mbe = new(item.UnwrapErr());
    240                     return Maybe<T>.None();
    241                 } else {
    242                     return Maybe<T>.Some(item.Unwrap());
    243                 }
    244             }));
    245             return mbe.IsSome ? new Result<TOK, TErr>(mbe.Unwrap()) : new Result<TOK, TErr>(v);
    246         }
    247         public static bool Ge<TOK, TErr, TOK2, TErr2>(in this Result<TOK, TErr> self, in Result<TOK2, TErr2> other) where TOK: notnull, IPartialOrd<TOK, TOK2> where TErr: notnull, IPartialOrd<TErr, TErr2> where TOK2: notnull, IPartialOrd<TOK2, TOK> where TErr2: notnull, IPartialOrd<TErr2, TErr> => (self.IsOK, other.IsOK) switch {
    248             (true, true) => self._ok >= other._ok,
    249             (true, false) => true,
    250             (false, true) => false,
    251             _ => self._err >= other._err,
    252         };
    253         public static int GetHashCodeEquatable<TOK, TErr>(in this Result<TOK, TErr> self) where TOK: notnull, IEquatable<TOK> where TErr: notnull, IEquatable<TErr> => self.IsOK ? HashCode.Combine(0, self._ok) : HashCode.Combine(1, self._err);
    254         public static bool Gt<TOK, TErr, TOK2, TErr2>(in this Result<TOK, TErr> self, in Result<TOK2, TErr2> other) where TOK: notnull, IPartialOrd<TOK, TOK2> where TErr: notnull, IPartialOrd<TErr, TErr2> where TOK2: notnull, IPartialOrd<TOK2, TOK> where TErr2: notnull, IPartialOrd<TErr2, TErr> => (self.IsOK, other.IsOK) switch {
    255             (true, true) => self._ok > other._ok,
    256             (true, false) => true,
    257             (false, true) => false,
    258             _ => self._err > other._err,
    259         };
    260         public static Unit Hash<TOK, TErr, THasher>(in this Result<TOK, TErr> self, ref THasher hasher) where TOK: notnull, IHashable where TErr: notnull, IHashable where THasher: notnull, IHasher {
    261 
    262             if (self.IsOK) {
    263                 _ = hasher.WriteByte(byte.MinValue);
    264                 return self._ok.Hash(ref hasher);
    265             } else {
    266                 _ = hasher.WriteByte(byte.MaxValue);
    267                 return self._err.Hash(ref hasher);
    268             }
    269         }
    270         public static string IntoString<TOK, TErr>(in this Result<TOK, TErr> self) where TOK: notnull, IInto<string> where TErr: notnull, IInto<string> => self.IsOK ? $"OK({self._ok.Into()})" : $"Err({self._err.Into()})";
    271         public static bool Le<TOK, TErr, TOK2, TErr2>(in this Result<TOK, TErr> self, in Result<TOK2, TErr2> other) where TOK: notnull, IPartialOrd<TOK, TOK2> where TErr: notnull, IPartialOrd<TErr, TErr2> where TOK2: notnull, IPartialOrd<TOK2, TOK> where TErr2: notnull, IPartialOrd<TErr2, TErr> => (self.IsOK, other.IsOK) switch {
    272             (true, true) => self._ok <= other._ok,
    273             (true, false) => false,
    274             (false, true) => true,
    275             _ => self._err <= other._err,
    276         };
    277         public static bool Lt<TOK, TErr, TOK2, TErr2>(in this Result<TOK, TErr> self, in Result<TOK2, TErr2> other) where TOK: notnull, IPartialOrd<TOK, TOK2> where TErr: notnull, IPartialOrd<TErr, TErr2> where TOK2: notnull, IPartialOrd<TOK2, TOK> where TErr2: notnull, IPartialOrd<TErr2, TErr> => (self.IsOK, other.IsOK) switch {
    278             (true, true) => self._ok < other._ok,
    279             (true, false) => false,
    280             (false, true) => true,
    281             _ => self._err < other._err,
    282         };
    283         public static bool Ne<TOK, TErr, TOK2, TErr2>(in this Result<TOK, TErr> self, in Result<TOK2, TErr2> other) where TOK: notnull, IPartialEq<TOK, TOK2> where TErr: notnull, IPartialEq<TErr, TErr2> where TOK2: notnull, IPartialEq<TOK2, TOK> where TErr2: notnull, IPartialEq<TErr2, TErr> => (self.IsOK, other.IsOK) switch {
    284             (true, true) => self._ok != other._ok,
    285             (false, false) => self._err != other._err,
    286             _ => true,
    287         };
    288         public static Maybe<Ordering> PartialCmp<TOK, TErr, TOK2, TErr2>(in this Result<TOK, TErr> self, in Result<TOK2, TErr2> other) where TOK: notnull, IPartialOrd<TOK, TOK2> where TErr: notnull, IPartialOrd<TErr, TErr2> where TOK2: notnull, IPartialOrd<TOK2, TOK> where TErr2: notnull, IPartialOrd<TErr2, TErr> => (self.IsOK, other.IsOK) switch {
    289             (true, true) => self._ok.PartialCmp(in other._ok),
    290             (true, false) => Some(Greater),
    291             (false, true) => Some(Less),
    292             _ => self._err.PartialCmp(in other._err),
    293         };
    294         public static Maybe<Result<TSome, TErr>> Transpose<TSome, TErr>(this Result<Maybe<TSome>, TErr> self) where TSome: notnull where TErr: notnull => self.IsOK ? self._ok.IsSome ? new(new(self._ok._some)) : Maybe<Result<TSome, TErr>>.None() : new(new(self._err));
    295         public static Result<string, TErr2> TryIntoString<TOK, TErr, TErr2>(in this Result<TOK, TErr> self) where TOK: notnull, ITryInto<string, TErr2> where TErr: notnull, ITryInto<string, TErr2> where TErr2: notnull {
    296 
    297             if (self.IsOK) {
    298                 var val = self._ok.TryInto();
    299                 return val.IsErr ? val : new($"OK({val._ok})");
    300             } else {
    301                 var val = self._err.TryInto();
    302                 return val.IsErr ? val : new($"Err({val._ok})");
    303             }
    304         }
    305         public static TOK UnwrapOrDefault<TOK, TErr>(this Result<TOK, TErr> self) where TOK: notnull, new() where TErr: notnull => self.Var == Result<TOK, TErr>.Tag.OK ? self._ok : new TOK();
    306         #endregion
    307 
    308         #region Instance Functions
    309         #endregion
    310 
    311         #region Operators
    312         #endregion
    313 
    314         #region Types
    315         #endregion
    316     }
    317     #endregion
    318 
    319     #region Namespaces
    320     #endregion
    321 }
    322 #endregion