Maybe.cs (16867B)
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.Ops; 8 using static Std.Maybe.Maybe<Std.Cmp.Ordering>; 9 using Std.Result; 10 using System; 11 using System.Runtime.CompilerServices; 12 using System.Runtime.InteropServices; 13 #region Namespaces 14 namespace Std.Maybe { 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 readonly ulong Len() => _maybe.IsSome ? 1ul : ulong.MinValue; 54 public Maybe<T> Next() { 55 56 var val = _maybe; 57 _maybe = Maybe<T>.None(); 58 return val; 59 } 60 public Maybe<T> NextBack() => Next(); 61 public TInit RFold<TInit>(TInit init, Fn<TInit, T, TInit> f) where TInit: notnull => IDoubleEndedIterator<T>.RFoldDefault(ref this, init, f); 62 public readonly Prod<ulong, Maybe<ulong>> SizeHint() => _maybe.IsSome ? new(1ul, new(1ul)) : new(ulong.MinValue, new(ulong.MinValue)); 63 public override readonly string ToString() => string.Empty; 64 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); 65 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); 66 readonly Result<IntoIterator<T>, Bottom> ITryInto<IntoIterator<T>, Bottom>.TryInto() => new(this); 67 #endregion 68 69 #region Operators 70 #endregion 71 72 #region Types 73 #endregion 74 } 75 // Even though TSome can be a non-readonly struct, we can still make Maybe readonly without any performance hit so long 76 // as we never access any members of _some. 77 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 0)] 78 public readonly struct Maybe<TSome>: ISum<TSome, Unit>, IInto<Maybe<TSome>>, IIntoIterator<TSome, IntoIterator<TSome>> where TSome: notnull { 79 80 #region Type-level Constructors 81 #endregion 82 83 #region Instance Constructors 84 [MethodImpl(MethodImplOptions.AggressiveInlining)] 85 public Maybe() => (_some, Var) = (default!, Tag.None); 86 [MethodImpl(MethodImplOptions.AggressiveInlining)] 87 public Maybe(TSome val) => (Var, _some) = (Tag.Some, val); 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 Maybes, we decide to expose this field 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 IsSome is true first though. 98 internal readonly TSome _some; 99 public readonly Tag Var; 100 #endregion 101 102 #region Type-level Properties 103 #endregion 104 105 #region Instance Properties 106 public readonly bool IsNone => Var == Tag.None; 107 public readonly bool IsSome => Var == Tag.Some; 108 public readonly Var2 Variant => Var == Tag.Some ? Var2.V0 : Var2.V1; 109 public readonly TSome Variant0 => Var == Tag.Some ? _some : throw new InvalidOperationException($"The Maybe variant, {Var.ToString()} is not Some!"); 110 public readonly Unit Variant1 => Var == Tag.None ? new Unit() : throw new InvalidOperationException($"The Maybe variant, {Var.ToString()} is not None!"); 111 #endregion 112 113 #region Type-level Functions 114 [MethodImpl(MethodImplOptions.AggressiveInlining)] 115 public static Maybe<TSome> None() => new(); 116 [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 public static Maybe<TSome> Some(TSome val) => new(val); 118 #endregion 119 120 #region Instance Functions 121 [MethodImpl(MethodImplOptions.AggressiveInlining)] 122 public readonly Maybe<T> And<T>(Maybe<T> val) where T: notnull => IsSome ? val : Maybe<T>.None(); 123 [MethodImpl(MethodImplOptions.AggressiveInlining)] 124 public readonly Maybe<T> AndThen<T>(Fn<TSome, Maybe<T>> f) where T: notnull => IsSome ? f(_some) : Maybe<T>.None(); 125 public override readonly bool Equals(object? _) => false; 126 [MethodImpl(MethodImplOptions.AggressiveInlining)] 127 public readonly TSome Expect(string msg) => IsSome ? _some : throw new InvalidOperationException(msg); 128 [MethodImpl(MethodImplOptions.AggressiveInlining)] 129 public readonly Unit ExpectNone(string msg) => !IsSome ? new Unit() : throw new InvalidOperationException($"{msg}: {_some.ToString()}"); 130 public readonly Maybe<TSome> Filter(FnIn<TSome, bool> f) => (IsSome && f(in _some)) ? this : new Maybe<TSome>(); 131 public override readonly int GetHashCode() => 0; 132 public readonly Maybe<TSome> Into() => this; 133 public readonly IntoIterator<TSome> IntoIter() => new(this); 134 [MethodImpl(MethodImplOptions.AggressiveInlining)] 135 public readonly Maybe<T> Map<T>(Fn<TSome, T> f) where T: notnull => IsSome ? new(f(_some)) : Maybe<T>.None(); 136 [MethodImpl(MethodImplOptions.AggressiveInlining)] 137 public readonly T MapOr<T>(T Default, Fn<TSome, T> f) where T: notnull => IsSome ? f(_some) : Default; 138 [MethodImpl(MethodImplOptions.AggressiveInlining)] 139 public readonly T MapOrElse<T>(Fn<T> Default, Fn<TSome, T> f) where T: notnull => IsSome ? f(_some) : Default(); 140 [MethodImpl(MethodImplOptions.AggressiveInlining)] 141 public readonly Result<TSome, TErr> OKOr<TErr>(TErr err) where TErr: notnull => IsSome ? new(_some) : new(err); 142 [MethodImpl(MethodImplOptions.AggressiveInlining)] 143 public readonly Result<TSome, TErr> OKOrElse<TErr>(Fn<TErr> err) where TErr: notnull => IsSome ? new(_some) : new(err()); 144 [MethodImpl(MethodImplOptions.AggressiveInlining)] 145 public readonly Maybe<TSome> Or(Maybe<TSome> val) => IsSome ? this : val; 146 [MethodImpl(MethodImplOptions.AggressiveInlining)] 147 public readonly Maybe<TSome> OrElse(Fn<Maybe<TSome>> f) => IsSome ? this : f(); 148 public override readonly string ToString() => IsSome ? $"Some({_some.ToString()})" : "None"; 149 [MethodImpl(MethodImplOptions.AggressiveInlining)] 150 public readonly TSome Unwrap() => IsSome ? _some : throw new InvalidOperationException("None"); 151 [MethodImpl(MethodImplOptions.AggressiveInlining)] 152 public readonly Unit UnwrapNone() => !IsSome ? new Unit() : throw new InvalidOperationException($"OK{_some.ToString()}"); 153 [MethodImpl(MethodImplOptions.AggressiveInlining)] 154 public readonly TSome UnwrapOr(TSome Default) => IsSome ? _some : Default; 155 [MethodImpl(MethodImplOptions.AggressiveInlining)] 156 public readonly TSome UnwrapOrElse(Fn<TSome> f) => IsSome ? _some : f(); 157 [MethodImpl(MethodImplOptions.AggressiveInlining)] 158 public readonly Maybe<TSome> Xor(Maybe<TSome> val) => !IsSome ? val : val.IsSome ? new Maybe<TSome>() : this; 159 public readonly Maybe<Prod<TSome, T>> Zip<T>(Maybe<T> other) where T: notnull => IsSome && other.IsSome ? new(new(_some, other._some)) : new Maybe<Prod<TSome, T>>(); 160 public readonly Maybe<TVal> ZipWith<T, TVal>(Maybe<T> other, Fn<TSome, T, TVal> f) where T: notnull where TVal: notnull => IsSome && other.IsSome ? new(f(_some, other._some)) : new Maybe<TVal>(); 161 readonly Result<Maybe<TSome>, Bottom> ITryInto<Maybe<TSome>, Bottom>.TryInto() => new(this); 162 #endregion 163 164 #region Operators 165 #endregion 166 167 #region Types 168 public enum Tag: byte { 169 None = byte.MinValue, 170 Some = byte.MaxValue, 171 } 172 #endregion 173 } 174 public static class Functions { 175 176 #region Type-level Constructors 177 #endregion 178 179 #region Instance Constructors 180 #endregion 181 182 #region Type-level Fields 183 #endregion 184 185 #region Instance Fields 186 #endregion 187 188 #region Type-level Properties 189 #endregion 190 191 #region Instance Properties 192 #endregion 193 194 #region Type-level Functions 195 public static Maybe<TSome> Clamp<TSome>(this Maybe<TSome> self, Maybe<TSome> min, Maybe<TSome> max) where TSome: notnull, IOrd<TSome> { 196 197 System.Diagnostics.Trace.Assert(min.Cmp(in max).Var != Ord.Greater); 198 return self.Cmp(in min).Var switch { 199 Ord.Less => min, 200 Ord.Equivalent => self, 201 _ => self.Cmp(in max).Var == Ord.Greater ? max : self, 202 }; 203 } 204 public static Maybe<TSome> Clone<TSome>(in this Maybe<TSome> self) where TSome: notnull, IClone<TSome> => self.IsSome ? new(self._some.Clone()) : new Maybe<TSome>(); 205 public static Ordering Cmp<TSome>(in this Maybe<TSome> self, in Maybe<TSome> other) where TSome: notnull, IOrd<TSome> => (self.IsSome, other.IsSome) switch { 206 (true, true) => self._some.Cmp(in other._some), 207 (true, false) => Greater, 208 (false, true) => Less, 209 _ => Equivalent, 210 }; 211 public static int CompareToComparable<TSome>(in this Maybe<TSome> self, in Maybe<TSome> other) where TSome: notnull, IComparable<TSome> => (self.IsSome, other.IsSome) switch { 212 (true, true) => self._some.CompareTo(other._some), 213 (true, false) => 1, 214 (false, true) => -1, 215 _ => 0, 216 }; 217 public static bool Contains<TSome, T>(in this Maybe<TSome> self, in T val) where TSome: notnull, IPartialEq<TSome, T> where T: notnull, IPartialEq<T, TSome> => self.IsSome && self._some == val; 218 public static bool Eq<T0, T1>(in this Maybe<T0> self, in Maybe<T1> other) where T0: notnull, IPartialEq<T0, T1> where T1: notnull, IPartialEq<T1, T0> => (self.IsSome, other.IsSome) switch { 219 (true, true) => self._some == other._some, 220 (false, false) => true, 221 _ => false, 222 }; 223 public static bool EqualsEquality<TSome>(in this Maybe<TSome> self, in Maybe<TSome> other) where TSome: notnull, IEquality<TSome> => (self.IsSome, other.IsSome) switch { 224 (true, true) => self._some.Equals(in other._some), 225 (false, false) => true, 226 _ => false, 227 }; 228 public static bool EqualsEquatable<T>(in this Maybe<T> self, in Maybe<T> other) where T: notnull, IEquatable<T> => (self.IsSome, other.IsSome) switch { 229 (true, true) => self._some.Equals(other._some), 230 (false, false) => true, 231 _ => false, 232 }; 233 public static Maybe<TSome> Flatten<TSome>(this Maybe<Maybe<TSome>> self) where TSome: notnull => self.AndThen(Convert.Functions.Identity); 234 public static Maybe<TSome> FromIter<TSome, T, TIter>(TIter iter) where T: notnull where TIter: notnull, IIterator<Maybe<T>> where TSome: notnull, IFromIterator<TSome, T> { 235 236 var mbe = new Maybe<Unit>(); 237 var v = TSome.FromIter(iter.Scan(new Unit(), (ref Unit _, ref Maybe<T> item) => { 238 if (item.IsNone) { mbe = new(new Unit()); } 239 return item; 240 })); 241 return mbe.IsSome ? new Maybe<TSome>() : new(v); 242 } 243 public static bool Ge<T0, T1>(in this Maybe<T0> self, in Maybe<T1> other) where T0: notnull, IPartialOrd<T0, T1> where T1: notnull, IPartialOrd<T1, T0> => (self.IsSome, other.IsSome) switch { 244 (true, true) => self._some >= other._some, 245 (true, false) => true, 246 (false, true) => false, 247 _ => true, 248 }; 249 public static int GetHashCodeEquatable<TSome>(in this Maybe<TSome> self) where TSome: notnull, IEquatable<TSome> => self.IsSome ? HashCode.Combine(1, self._some) : 0; 250 public static bool Gt<T0, T1>(in this Maybe<T0> self, in Maybe<T1> other) where T0: notnull, IPartialOrd<T0, T1> where T1: notnull, IPartialOrd<T1, T0> => (self.IsSome, other.IsSome) switch { 251 (true, true) => self._some > other._some, 252 (true, false) => true, 253 (false, true) => false, 254 _ => false, 255 }; 256 public static Unit Hash<TSome, THasher>(in this Maybe<TSome> self, ref THasher hasher) where TSome: notnull, IHashable where THasher: notnull, IHasher { 257 258 if (self.IsSome) { 259 _ = hasher.WriteByte(byte.MaxValue); 260 return self._some.Hash(ref hasher); 261 } else { 262 return hasher.WriteByte(byte.MinValue); 263 } 264 } 265 public static string IntoString<TSome>(in this Maybe<TSome> self) where TSome: notnull, IInto<string> => self.IsSome ? $"Some({self._some.Into()})" : "None"; 266 public static bool Le<T0, T1>(in this Maybe<T0> self, in Maybe<T1> other) where T0: notnull, IPartialOrd<T0, T1> where T1: notnull, IPartialOrd<T1, T0> => (self.IsSome, other.IsSome) switch { 267 (true, true) => self._some <= other._some, 268 (true, false) => false, 269 (false, true) => true, 270 _ => true, 271 }; 272 public static bool Lt<T0, T1>(in this Maybe<T0> self, in Maybe<T1> other) where T0: notnull, IPartialOrd<T0, T1> where T1: notnull, IPartialOrd<T1, T0> => (self.IsSome, other.IsSome) switch { 273 (true, true) => self._some < other._some, 274 (true, false) => false, 275 (false, true) => true, 276 _ => false, 277 }; 278 public static bool Ne<T0, T1>(in this Maybe<T0> self, in Maybe<T1> other) where T0: notnull, IPartialEq<T0, T1> where T1: notnull, IPartialEq<T1, T0> => (self.IsSome, other.IsSome) switch { 279 (true, true) => self._some != other._some, 280 (false, false) => false, 281 _ => true, 282 }; 283 public static Maybe<Ordering> PartialCmp<T0, T1>(in this Maybe<T0> self, in Maybe<T1> other) where T0: notnull, IPartialOrd<T0, T1> where T1: notnull, IPartialOrd<T1, T0> => (self.IsSome, other.IsSome) switch { 284 (true, true) => self._some.PartialCmp(in other._some), 285 (true, false) => Some(Greater), 286 (false, true) => Some(Less), 287 _ => Some(Equivalent), 288 }; 289 public static Result<Maybe<TOK>, TErr> Transpose<TOK, TErr>(this Maybe<Result<TOK, TErr>> self) where TOK: notnull where TErr: notnull => self.IsSome ? self._some.IsOK ? new(new Maybe<TOK>(self._some._ok)) : new(self._some._err) : new(new Maybe<TOK>()); 290 public static Result<string, TErr> TryIntoString<TSome, TErr>(in this Maybe<TSome> self) where TSome: notnull, ITryInto<string, TErr> where TErr: notnull { 291 292 if (self.IsSome) { 293 var val = self._some.TryInto(); 294 return val.IsErr ? val : new($"Some({val._ok})"); 295 } else { 296 return new("None"); 297 } 298 } 299 public static TSome UnwrapOrDefault<TSome>(this Maybe<TSome> self) where TSome: notnull, new() => self.Var == Maybe<TSome>.Tag.Some ? self._some : new TSome(); 300 #endregion 301 302 #region Instance Functions 303 #endregion 304 305 #region Operators 306 #endregion 307 308 #region Types 309 #endregion 310 } 311 #endregion 312 313 #region Namespaces 314 #endregion 315 } 316 #endregion