diff --git a/.vscode/settings.json b/.vscode/settings.json index 9e26dfe..6b00587 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1 +1,187 @@ -{} \ No newline at end of file +{ + "[csharp]": { + "editor.codeActionsOnSave": { + "source.addMissingImports": "explicit", + "source.fixAll": "explicit", + "source.organizeImports": "explicit" + }, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.formatOnType": false + }, + "emeraldwalk.runonsave": { + "commands": [ + { + // run `clang-format` on Godot shader files when saving + "match": "\\.gdshader|\\.gdshaderinc$", + "cmd": "clang-format -i '${file}'" + } + ] + }, + "csharp.debug.justMyCode": false, + "csharp.debug.suppressJITOptimizations": true, + "csharp.semanticHighlighting.enabled": true, + "dotnet.backgroundAnalysis.analyzerDiagnosticsScope": "fullSolution", + "dotnet.backgroundAnalysis.compilerDiagnosticsScope": "fullSolution", + "dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true, + "dotnet.enableXamlTools": false, + "dotnet.formatting.organizeImportsOnFormat": true, + "dotnet.preferCSharpExtension": true, + "dotnet.server.useOmnisharp": false, // You decide + "dotnetAcquisitionExtension.enableTelemetry": false, + "editor.semanticHighlighting.enabled": true, + // C# doc comment colorization gets lost with semantic highlighting, but we + // need semantic highlighting for proper syntax highlighting with record + // shorthand. + // + // Here's a workaround for doc comment highlighting from + // https://github.com/OmniSharp/omnisharp-vscode/issues/3816 + "editor.tokenColorCustomizations": { + "[*]": { + // Themes that don't include the word "Dark" or "Light" in them. + // These are some bold colors that show up well against most dark and + // light themes. + // + // Change them to something that goes well with your preferred theme :) + "textMateRules": [ + { + "scope": "comment.documentation", + "settings": { + "foreground": "#0091ff" + } + }, + { + "scope": "comment.documentation.attribute", + "settings": { + "foreground": "#8480ff" + } + }, + { + "scope": "comment.documentation.cdata", + "settings": { + "foreground": "#0091ff" + } + }, + { + "scope": "comment.documentation.delimiter", + "settings": { + "foreground": "#aa00ff" + } + }, + { + "scope": "comment.documentation.name", + "settings": { + "foreground": "#ef0074" + } + } + ] + }, + "[*Dark*]": { + // Themes that include the word "Dark" in them. + "textMateRules": [ + { + "scope": "comment.documentation", + "settings": { + "foreground": "#608B4E" + } + }, + { + "scope": "comment.documentation.attribute", + "settings": { + "foreground": "#C8C8C8" + } + }, + { + "scope": "comment.documentation.cdata", + "settings": { + "foreground": "#E9D585" + } + }, + { + "scope": "comment.documentation.delimiter", + "settings": { + "foreground": "#808080" + } + }, + { + "scope": "comment.documentation.name", + "settings": { + "foreground": "#569CD6" + } + } + ] + }, + "[*Light*]": { + // Themes that include the word "Light" in them. + "textMateRules": [ + { + "scope": "comment.documentation", + "settings": { + "foreground": "#008000" + } + }, + { + "scope": "comment.documentation.attribute", + "settings": { + "foreground": "#282828" + } + }, + { + "scope": "comment.documentation.cdata", + "settings": { + "foreground": "#808080" + } + }, + { + "scope": "comment.documentation.delimiter", + "settings": { + "foreground": "#808080" + } + }, + { + "scope": "comment.documentation.name", + "settings": { + "foreground": "#808080" + } + } + ] + } + }, + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.expand": false, + "explorer.fileNesting.patterns": { + "*": "$(capture).import, $(capture).uid" + }, + "markdownlint.config": { + // Allow non-unique heading names so we don't break the changelog. + "MD024": false, + // Allow html in markdown. + "MD033": false + }, + "markdownlint.ignore": [ + "**/LICENSE" + ], + "omnisharp.enableEditorConfigSupport": true, + "omnisharp.enableMsBuildLoadProjectsOnDemand": false, + "omnisharp.maxFindSymbolsItems": 3000, + "omnisharp.useModernNet": false, + // Remove these if you're happy with your terminal profiles. + "terminal.integrated.defaultProfile.windows": "Git Bash", + "terminal.integrated.profiles.windows": { + "Command Prompt": { + "icon": "terminal-cmd", + "path": [ + "${env:windir}\\Sysnative\\cmd.exe", + "${env:windir}\\System32\\cmd.exe" + ] + }, + "Git Bash": { + "icon": "terminal", + "source": "Git Bash" + }, + "PowerShell": { + "icon": "terminal-powershell", + "source": "PowerShell" + } + } +} diff --git a/Functional/Cache.cs b/Functional/Cache.cs index 6318913..4ceea19 100644 --- a/Functional/Cache.cs +++ b/Functional/Cache.cs @@ -1,17 +1,17 @@ namespace SJK.Functional; +// [Obsolete] +// public class Cache where T : class +// { +// private T? _cache; +// private Func _provider; +// private readonly Func? _isvalid; -public class Cache where T : class -{ - private T? _cache; - private Func _provider; - private readonly Func? _isvalid; +// public Cache(Func provider, Func? isvalid = null) +// { +// _provider = provider; +// _isvalid = isvalid; +// } +// public bool IsCached() => _cache is not null; +// public T Value => _cache is null || (_isvalid is not null && !_isvalid(_cache)) ? _cache = _provider() : _cache; - public Cache(Func provider, Func? isvalid = null) - { - _provider = provider; - _isvalid = isvalid; - } - public bool IsCached() => _cache is not null; - public T Value => _cache is null || (_isvalid is not null && !_isvalid(_cache)) ? _cache = _provider() : _cache; - -} \ No newline at end of file +// } \ No newline at end of file diff --git a/Functional/IEither.cs b/Functional/IEither.cs index ab9743b..c1d6d35 100644 --- a/Functional/IEither.cs +++ b/Functional/IEither.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace SJK.Functional; public interface IEither @@ -12,3 +14,140 @@ public interface IEither bool IsRight(); bool IsLeft() => !IsRight(); } +// public class test +// { +// public static void Main() +// { +// var res = TestEither(); +// if (res.Ok) +// { + +// } +// res.Match(success => Option.Some(success), error => Option.None()); +// } +// public static Result TestEither() +// { +// return new Result(); +// } +// } +public struct Either +{ + private TLeft? _left; + private TRight? _right; + [MemberNotNullWhen(true,nameof(_left)), MemberNotNullWhen(false,nameof(_right))] + public bool IsLeft {get;init;} + public bool IsRight => !IsLeft; + private Either(TLeft? _left, TRight? right, bool isLeft) + { + this._left = _left; + _right = right; + IsLeft = isLeft; + // Union.Create(5,union => + // { + // union.Set(5.0); + // var s = union.Match( + // i => i.ToString(), + // i => i.ToString(), + // i => i.ToString() + // ); + // }); + } + public (Option left, Option right) Deconstruct() => (_left,_right); + [MemberNotNullWhen(true,nameof(_right)),MemberNotNullWhen(false,nameof(_left))] + public TLeft Reduce(Func mapping) => IsLeft?_left:mapping(_right); + public TResult Match(Func left, Func right) => IsLeft ? left(_left) : right(_right); + Either MapLeft(Func mapping) => IsLeft? Either.Left(mapping(_left)) :Either.Right(_right); + Either MapRight(Func mapping) => IsLeft? Either.Left(_left) :Either.Right(mapping(_right)); + public static Either Left(TLeft left) => new(left,default,true); + public static Either Right(TRight right) => new(default,right,false); +} + +public ref struct Union where T : unmanaged where T2 : unmanaged where T3 : unmanaged +{ + private byte _type; + private Span _data; + public static unsafe int SizeInBytes => System.Math.Max(System.Math.Max(sizeof(T),sizeof(T2)),sizeof(T3)); + public Union(Span buffer) + { + _data = buffer; + } + public Union() + { + _data = new byte[SizeInBytes]; + } + public static void Create(T value,Action> action) + { + Span span = stackalloc byte[SizeInBytes]; + var union = new Union(span); + union.Set(value); + action(union); + } + public static void Create(T2 value,Action> action) + { + Span span = stackalloc byte[SizeInBytes]; + var union = new Union(span); + union.Set(value); + action(union); + } + public static void Create(T3 value,Action> action) + { + Span span = stackalloc byte[SizeInBytes]; + var union = new Union(span); + union.Set(value); + action(union); + } + public void Set(in T value) + { + System.Runtime.InteropServices.MemoryMarshal.Write(_data, value); + _type = 1; + } + + public void Set(in T2 value) + { + System.Runtime.InteropServices.MemoryMarshal.Write(_data, value); + _type = 2; + } + + public void Set(in T3 value) + { + System.Runtime.InteropServices.MemoryMarshal.Write(_data, value); + _type = 3; + } + public void Match( + Action t, + Action t2, + Action t3 + ) + { + switch(_type) + { + case 1: + t(As()); + break; + case 2: + t2(As()); + break; + case 3: + t3(As()); + break; + }; + } + public TResult Match( + Func t, + Func t2, + Func t3 + ) => _type switch + { + 1=>t(As()), + 2=>t2(As()), + 3=>t3(As()), + _ => throw new NotSupportedException() + }; + + + public ref T As() + where T : unmanaged + { + return ref System.Runtime.InteropServices.MemoryMarshal.Cast(_data)[0]; + } +} \ No newline at end of file diff --git a/Functional/IOption.cs b/Functional/IOption.cs index ba343dc..1c490aa 100644 --- a/Functional/IOption.cs +++ b/Functional/IOption.cs @@ -1,51 +1,51 @@ -using System.Diagnostics.CodeAnalysis; +// using System.Diagnostics.CodeAnalysis; -namespace SJK.Functional; -public interface IOption : IEquatable> -{ - /// - /// Applies a function to the contained value if it exists (is not None), otherwise applies a default function. - /// - /// Function to apply if the option contains a value. - /// Function to apply if the option does not contain a value. - /// The result of applying either the onSome or onNone function. - TResult Match(Func onSome, Func onNone); - void Match(Action onSome,Action onNone); +// namespace SJK.Functional; +// public interface IOption : IEquatable> +// { +// /// +// /// Applies a function to the contained value if it exists (is not None), otherwise applies a default function. +// /// +// /// Function to apply if the option contains a value. +// /// Function to apply if the option does not contain a value. +// /// The result of applying either the onSome or onNone function. +// TResult Match(Func onSome, Func onNone); +// void Match(Action onSome,Action onNone); - /// - /// Chains operations on the contained value using a function that returns an option. - /// - /// A function that takes the contained value and returns an option. - /// An option resulting from applying the function f to the contained value. - IOption Bind(Func> f); +// /// +// /// Chains operations on the contained value using a function that returns an option. +// /// +// /// A function that takes the contained value and returns an option. +// /// An option resulting from applying the function f to the contained value. +// IOption Bind(Func> f); - /// - /// Maps a function over the contained value if it exists (is not None). - /// - /// The function to apply to the contained value. - /// An option containing the result of applying f to the contained value. - IOption Map(Func f); +// /// +// /// Maps a function over the contained value if it exists (is not None). +// /// +// /// The function to apply to the contained value. +// /// An option containing the result of applying f to the contained value. +// IOption Map(Func f); - /// - /// Returns the contained value if it exists (is not None), otherwise returns the provided default value. - /// - /// The default value to return if the option does not contain a value. - /// The contained value or the default value. - T Or(T aDefault); +// /// +// /// Returns the contained value if it exists (is not None), otherwise returns the provided default value. +// /// +// /// The default value to return if the option does not contain a value. +// /// The contained value or the default value. +// T Or(T aDefault); - /// - /// Returns the contained value if it exists (is not None), otherwise calls the provided default function. - /// - /// The default value to return if the option does not contain a value. - /// The contained value or the default value. - T Or(Func aDefault); +// /// +// /// Returns the contained value if it exists (is not None), otherwise calls the provided default function. +// /// +// /// The default value to return if the option does not contain a value. +// /// The contained value or the default value. +// T Or(Func aDefault); - /// - /// Checks if the option contains a value (is not None) and outputs it if true. - /// - /// An out parameter that will be assigned the contained value if it exists. - /// true if the option contains a value; false otherwise. - bool HasValue([NotNullWhen(true)]out T? value); - // [Obsolete] - bool HasValue(); -} +// /// +// /// Checks if the option contains a value (is not None) and outputs it if true. +// /// +// /// An out parameter that will be assigned the contained value if it exists. +// /// true if the option contains a value; false otherwise. +// bool HasValue([NotNullWhen(true)]out T? value); +// // [Obsolete] +// bool HasValue(); +// } diff --git a/Functional/Left.cs b/Functional/Left.cs index 6e9b615..594f34f 100644 --- a/Functional/Left.cs +++ b/Functional/Left.cs @@ -1,42 +1,42 @@ -namespace SJK.Functional; +// namespace SJK.Functional; -public sealed class Left : IEither -{ - private readonly TLeft _value; - public Left(TLeft value){ - this._value = value; - } +// public sealed class Left : IEither +// { +// private readonly TLeft _value; +// public Left(TLeft value){ +// this._value = value; +// } - public IEither Bind(Func> value) - { - return new Left(_value); - } +// public IEither Bind(Func> value) +// { +// return new Left(_value); +// } - public (TLeft?, TRight?) Deconstruct()=>(_value,default(TRight)); +// public (TLeft?, TRight?) Deconstruct()=>(_value,default(TRight)); - public bool IsRight() => true; +// public bool IsRight() => true; - public IEither MapLeft(Func mapping) - => new Left(mapping(this._value)); +// public IEither MapLeft(Func mapping) +// => new Left(mapping(this._value)); - public IEither MapRight(Func mapping) - => new Left(this._value); +// public IEither MapRight(Func mapping) +// => new Left(this._value); - public TResult Match(Func left, Func right) - { - return left(_value); - } +// public TResult Match(Func left, Func right) +// { +// return left(_value); +// } - public void Match(Action left, Action right) - { - left(_value); - } +// public void Match(Action left, Action right) +// { +// left(_value); +// } - public TLeft Reduce(Func mapping)=> _value; - public override string ToString() - { - return $"Left<{typeof(TLeft).Name}> with Value {_value}"; - } -} +// public TLeft Reduce(Func mapping)=> _value; +// public override string ToString() +// { +// return $"Left<{typeof(TLeft).Name}> with Value {_value}"; +// } +// } diff --git a/Functional/None.cs b/Functional/None.cs index c0d80b7..0b01e20 100644 --- a/Functional/None.cs +++ b/Functional/None.cs @@ -1,31 +1,31 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; +// using System.Diagnostics.CodeAnalysis; +// using System.Runtime.CompilerServices; -namespace SJK.Functional; +// namespace SJK.Functional; -public sealed class None : IOption -{ +// public sealed class None : IOption +// { - public static None Of() => new(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TResult Match(Func _, Func onNone) => - onNone(); +// public static None Of() => new(); +// [MethodImpl(MethodImplOptions.AggressiveInlining)] +// public TResult Match(Func _, Func onNone) => +// onNone(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Match(Action onSome, Action onNone) => onNone(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IOption Bind(Func> f) => new None(); -[MethodImpl(MethodImplOptions.AggressiveInlining)] - public IOption Map(Func f) => new None(); -[MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Or(T aDefault) => aDefault; - public T Or(Func aDefault)=>aDefault(); -public bool HasValue([NotNullWhen(true)]out T? value) => (value = default) is not null && false; -public bool HasValue() => false; - public override string ToString() - { - return nameof(None); - } +// [MethodImpl(MethodImplOptions.AggressiveInlining)] +// public void Match(Action onSome, Action onNone) => onNone(); +// [MethodImpl(MethodImplOptions.AggressiveInlining)] +// public IOption Bind(Func> f) => new None(); +// [MethodImpl(MethodImplOptions.AggressiveInlining)] +// public IOption Map(Func f) => new None(); +// [MethodImpl(MethodImplOptions.AggressiveInlining)] +// public T Or(T aDefault) => aDefault; +// public T Or(Func aDefault)=>aDefault(); +// public bool HasValue([NotNullWhen(true)]out T? value) => (value = default) is not null && false; +// public bool HasValue() => false; +// public override string ToString() +// { +// return nameof(None); +// } - public bool Equals(IOption? other)=> other is None; -} +// public bool Equals(IOption? other)=> other is None; +// } diff --git a/Functional/Option.cs b/Functional/Option.cs new file mode 100644 index 0000000..f414214 --- /dev/null +++ b/Functional/Option.cs @@ -0,0 +1,67 @@ + + +using System.Diagnostics.CodeAnalysis; + +namespace SJK.Functional; + +public readonly struct Option : IOption, + IOption, T>//, + // K>, + // IEquatable> +{ + private readonly T? _value; + private Option(T? value, bool hasValue) + { + _value = value; + HasValue = hasValue; + } + public static Option None => new Option(default!,false); + + public T? Value => _value; + + [MemberNotNullWhen(true, nameof(HasValue), nameof(_value))] + public bool HasValue {get;init;} + + public static Option Some(T value) => new Option(value, true); + + + // public bool Equals(Option other) + // { + // return other.HasValue == HasValue && EqualityComparer.Default.Equals(_value,other._value); + // } + + + public TResult Match(Func onSome, Func onNone) => HasValue ? onSome(_value) : onNone(); + + + // public TOption Map(Func f) where TOption : IOption => HasValue ? TOption.Some(f(Value)) : TOption.None; + // public Option Map(Func f) => HasValue ? Option.Some(f(Value)) : Option.None; + + public Option Map(Func f) => HasValue ? Option.Some(f(_value)) : Option.None; + public Option Bind(Func> f) => HasValue ? f(_value) : Option.None; + + public static implicit operator Option (T? nullable) => nullable is not null?Some(nullable) : None; + public static implicit operator T? (Option nullable) => nullable.Value; + // public static implicit operator bool(Option option) => option.HasValue; + public static bool operator true(Option value) => value.HasValue; + public static bool operator false(Option value) => !value.HasValue; + public override int GetHashCode()=> HasValue? _value.GetHashCode() : 0; + public override string ToString() => HasValue ? $"Some({_value})" : $"None<{typeof(T).Name}>()"; + // public override bool Equals([NotNullWhen(true)] object? obj) => obj is Option option?Equals(option):false; + // public T Or(Func aDefault) => HasValue?_value:aDefault(); + + // public T OrDefault(T aDefault) => HasValue?_value:aDefault; + + // IOption IOption.Map(Func f) => Match>(onSome => Some.Of(f(onSome)), ()=>None.Of); + + // public bool TryGetValue(out T value) + // { + // if (HasValue) + // { + // value = Value; + // return true; + // } + // value = default; + // return false; + // } +} diff --git a/Functional/OptionExtensions.cs b/Functional/OptionExtensions.cs index 290d71d..d4c56f5 100644 --- a/Functional/OptionExtensions.cs +++ b/Functional/OptionExtensions.cs @@ -1,10 +1,11 @@ using System.Diagnostics; +using System.Numerics; namespace SJK.Functional; -public static class OptionExtensions +public static partial class OptionExtensions { - public static IEnumerable Match(this IEnumerable> values, Func onSome, Func onNone) + public static IEnumerable Match(this IEnumerable> values, Func onSome, Func onNone) { foreach (var item in values) { @@ -19,24 +20,24 @@ public static class OptionExtensions // } // return None.Of(); // } - public static IOption GetValue(this IReadOnlyDictionary value, Tkey key) where Tkey : notnull + public static Option GetValueOrNone(this IReadOnlyDictionary value, Tkey key) where Tkey : notnull { if (value.TryGetValue(key, out var v)) { return v.ToOption(); } - return None.Of(); + return Option.None; } - public static IOption OrElse(this IOption option, Func> fallback) - { - return option.Match( - some => option, - () => fallback() - ); - } - public static IOption BindUsing( - this IOption option, - Func> func) + // public static Option OrElse(this Option option, Func> fallback) + // { + // return option.Match( + // some => option, + // () => fallback() + // ); + // } + public static Option BindUsing( + this Option option, + Func> func) where TDisposable : IDisposable { return option.Match( @@ -44,48 +45,48 @@ public static class OptionExtensions { using (some) return func(some); }, - () => None.Of() + () => Option.None ); } - public static IOption ToOption(this T? value) => value != null ? Some.Of(value) : None.Of(); - [StackTraceHidden] + // public static IOption ToOption(this T? value) => value != null ? Some.Of(value) : None.Of(); + // [StackTraceHidden] // [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T OrThrow(this IOption option, Func exceptionFactory) where TEx : Exception - { - if (option.HasValue(out var value)) - return value; + // public static T OrThrow(this IOption option, Func exceptionFactory) where TEx : Exception + // { + // if (option.HasValue(out var value)) + // return value; - throw exceptionFactory(); - } - public static IEither ToEither(this Tvalue value, Func isSuccess, Func ok, Func err) - { - return isSuccess(value) ? new Right(ok()) : new Left(err(value)); - } - public static IEither ToEither(this Tvalue value, Tvalue successValue, Func ok, Func err) where Tvalue : notnull - { - return value.ToEither(e => e.Equals(successValue), ok, err); - } + // throw exceptionFactory(); + // } + // public static IEither ToEither(this Tvalue value, Func isSuccess, Func ok, Func err) + // { + // return isSuccess(value) ? new Right(ok()) : new Left(err(value)); + // } + // public static IEither ToEither(this Tvalue value, Tvalue successValue, Func ok, Func err) where Tvalue : notnull + // { + // return value.ToEither(e => e.Equals(successValue), ok, err); + // } // public static IEnumerable> ToOptions(this IEnumerable value) => value.Select(thing => thing.ToOption()); - public static Some ToSome(this T value) where T : notnull => Some.Of(value); - public static void IfSome(this IOption value, Action onSome) => value.Match(onSome, () => { }); - public static void IfNone(this IOption value, Action onNone) => value.Match(_ => { }, onNone); + // public static Some ToSome(this T value) where T : notnull => Some.Of(value); + // public static void IfSome(this IOption value, Action onSome) => value.Match(onSome, () => { }); + // public static void IfNone(this IOption value, Action onNone) => value.Match(_ => { }, onNone); - public static T Or(this IOption value, Func @default) => value.Or(@default()); - public static IEnumerable OnlySomes(this IEnumerable> options) + // public static T Or(this IOption value, Func @default) => value.Or(@default()); + public static IEnumerable OnlySomes(this IEnumerable> options) { foreach (var item in options) { - if (item.HasValue(out var item2)) - yield return item2; + if (item.HasValue) + yield return item.Value; } } // public static None ToNone(this T value) => None.Of(); - public static Option ToStructOption(this IOption option) => option.HasValue(out var value) ? Option.Some(value) : Option.None; - public static IOption ToClassOption(this Option option) => option.HasValue ? option.Value.ToOption() : None.Of(); - public static IOption FirstOrNone(this IEnumerable options) => options.Any() ? options.First().ToOption() : None.Of(); - public static IOption FirstOrNone(this IEnumerable options, Predicate predicate) + // public static Option ToStructOption(this IOption option) => option.HasValue(out var value) ? Option.Some(value) : Option.None; + // public static IOption ToClassOption(this Option option) => option.HasValue ? option.Value.ToOption() : None.Of(); + public static Option FirstOrNone(this IEnumerable options) => options.Any() ? options.First().ToOption() : Option.None; + public static Option FirstOrNone(this IEnumerable options, Predicate predicate) { foreach (var item in options) { @@ -94,6 +95,84 @@ public static class OptionExtensions return item.ToOption(); } } - return None.Of(); + return Option.None; + } + public static Option ToOption(this T? value) => value is null ? Option.None : Option.Some(value); + + + public static Option TryGetValue(this Option> option, T key) => option.Map(f => f.TryGetValue(key, out var result)?Option.Some(result):Option.None()); + + + extension(Option) where T : IAdditionOperators + { + public static Option operator +(Option left, T right) => left.Map(f => f + right); + } + extension(Option) where T : ISubtractionOperators + { + public static Option operator -(Option left, T right) => left.Map(f => f - right); + } + + extension(Option) where T : IMultiplyOperators + { + public static Option operator *(Option left, T right) => left.Map(f => f *right); + } + extension(Option) where T : IDivisionOperators + { + public static Option operator /(Option left, T right) => left.Map(f => f / right); + } + extension(Option) where T : IIncrementOperators + { + public static Option operator ++(Option left) => left.Map(f => f++); + } + + extension(Option) where T : IDecrementOperators + { + public static Option operator --(Option left) => left.Map(f => f--); + } + extension(Option) where T : IModulusOperators + { + public static Option operator %(Option left, T right) => left.Map(f => f % right); + } + extension(Option _) where T : IShiftOperators + { + public static Option operator <<(Option left, T right) => left.Map(f => f << right); + public static Option operator >>(Option left, T right) => left.Map(f => f >> right); + public static Option operator >>>(Option left, T right) => left.Map(f => f >>> right); + } + extension(Option) where T : IBitwiseOperators + { + public static Option operator &(Option left, T right) => left.Map(f => f & right); + public static Option operator |(Option left, T right) => left.Map(f => f | right); + public static Option operator ^(Option left, T right) => left.Map(f => f ^ right); + public static Option operator ~(Option left) => left.Map(f => ~f); + } + extension(Option) where T : IComparisonOperators + { + public static Option operator >(Option left, T right) => left.Map(f => f > right); + public static Option operator <(Option left, T right) => left.Map(f => f < right); + public static Option operator >=(Option left, T right) => left.Map(f => f >= right); + public static Option operator <=(Option left, T right) => left.Map(f => f <= right); + } + extension(Option) where T : IUnaryPlusOperators + { + public static Option operator +(Option left) => left.Map(f => +f); + } + extension(Option) where T : IUnaryNegationOperators + { + public static Option operator -(Option left) => left.Map(f => -f); + } + extension(in Option option) where T : IComparable + { + public int CompareTo(T? value) => option.Map(f => f.CompareTo(value)); + } + extension(in Option option) where T : IParsable + { + public static Option TryParse(in string? value, IFormatProvider formatProvider) => T.TryParse(value, formatProvider, out var result)?Option.Some(result):Option.None(); + public static Option Parse(in string value, IFormatProvider formatProvider) => T.Parse(value, formatProvider); + } + extension(Option) where T : IEqualityOperators + { + public static Option operator ==(Option left, T right) => left.Map(f => f == right); + public static Option operator !=(Option left, T right) => left.Map(f => f != right); } } diff --git a/Functional/OptionalStruct.cs b/Functional/OptionalStruct.cs index 5e722d0..dba989f 100644 --- a/Functional/OptionalStruct.cs +++ b/Functional/OptionalStruct.cs @@ -1,40 +1,104 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; +// using System.Diagnostics; +// using System.Diagnostics.CodeAnalysis; +// using System.Runtime.CompilerServices; +// using SJK.Functional; -namespace SJK.Functional; +// namespace SJK.Functional +// { +// /// +// /// A simple implementation of a monadic option, which can hold either a value of type T or no value. +// /// +// public struct Option : IEquatable> +// { +// private readonly T? _value; +// [MemberNotNullWhen(true, nameof(_value))] +// public bool HasValue { get; } +// public static Option None => new(default, false); +// public static Option Some(T value) => new(value, true); -public struct Option : IEquatable> -{ - private readonly T? _value; - [MemberNotNullWhen(true, nameof(_value))] - public bool HasValue { get; } - public static Option None => new(default, false); - public static Option Some(T value) => new(value, true); - private Option(T? value, bool hasValue) - { - Debug.Assert(!(value is null && hasValue), "Can not create an option with a null value had say it has one"); +// /// +// /// A constructor for creating an instance of the Option struct. +// /// +// private Option(T? value, bool hasValue) +// { +// Debug.Assert(!(value is null && hasValue), "Can not create an option with a null value had say it has one"); - _value = value; - HasValue = hasValue; - } - public T? Value => HasValue ? _value : throw new InvalidOperationException("Can not retrive value when there is no value."); +// _value = value; +// HasValue = hasValue; +// } - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TResult Match(Func some, Func none) => HasValue ? some(_value) : none(); - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Option Map(Func f) => HasValue ? Option.Some(f(_value)) : Option.None; - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Option Bind(Func> f) => HasValue ? f(_value) : Option.None; - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T OrDefault(T value) => HasValue ? _value : value; - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Or(Func factory) => HasValue ? _value : factory(); +// /// +// /// Gets the held value if it exists. Throws an exception if there's no value. +// /// +// public T? Value => HasValue ? _value : throw new InvalidOperationException("Can not retrieve value when there is no value."); - public override bool Equals([NotNullWhen(true)] object? obj) => obj is Option other && Equals(other); +// /// +// /// Applies a function to the held value if it exists, otherwise applies another function. +// /// +// /// The type of result produced by the functions. +// public TResult Match(Func some, Func none) +// { +// // If we have a value, apply 'some' to it. Otherwise, apply 'none'. +// return HasValue ? some(_value) : none(); +// } - public bool Equals(Option other) => (HasValue == other.HasValue) && (!HasValue || EqualityComparer.Default.Equals(_value, other._value)); +// /// +// /// Maps the held value if it exists using the given function, otherwise returns an empty option. +// /// +// /// The type of result produced by the mapping function. +// public Option Map(Func f) +// { +// // If we have a value, map it and wrap the result in another option. Otherwise, return an empty option. +// return HasValue ? Option.Some(f(_value)) : Option.None; +// } - public override int GetHashCode() => HasValue ? EqualityComparer.Default.GetHashCode(_value) : 0; - public override string ToString() => HasValue ? $"Some({_value})" : $"None<{typeof(T).Name}>()"; -} +// /// +// /// Binds the held value if it exists using the given function, otherwise returns an empty option. +// /// +// /// The type of result produced by the binding function. +// public Option Bind(Func> f) +// { +// // If we have a value, bind it and wrap the result in another option. Otherwise, return an empty option. +// return HasValue ? f(_value) : Option.None; +// } + +// /// +// /// Returns the held value if it exists, otherwise returns a default value. +// /// +// public T OrDefault(T value) +// { +// // If we have a value, return it. Otherwise, return the default value. +// return HasValue ? _value : value; +// } + +// /// +// /// Returns the held value if it exists, otherwise returns a result from a factory function. +// /// +// public T Or(Func factory) +// { +// // If we have a value, return it. Otherwise, invoke the factory function and return its result. +// return HasValue ? _value : factory(); +// } +// public override bool Equals([NotNullWhen(true)] object? obj) => obj is Option other && Equals(other); + +// /// +// /// Checks for equality with another option of type T. +// /// +// public bool Equals(Option other) +// { +// // If both options have values, check their equality. Otherwise, consider them equal if they're both empty. +// return (HasValue == other.HasValue) && (!HasValue || EqualityComparer.Default.Equals(_value, other._value)); +// } + +// public override int GetHashCode() => HasValue ? EqualityComparer.Default.GetHashCode(_value) : 0; + +// /// +// /// Returns a string representation of this option. +// /// +// public override string ToString() +// { +// // If we have a value, return it as a 'Some' with the value. Otherwise, return an empty 'None'. +// return HasValue ? $"Some({_value})" : $"None<{typeof(T).Name}>()"; +// } +// } +// } \ No newline at end of file diff --git a/Functional/RefOption.cs b/Functional/RefOption.cs new file mode 100644 index 0000000..53a0369 --- /dev/null +++ b/Functional/RefOption.cs @@ -0,0 +1,280 @@ + + +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SJK.Functional; +public interface IOption +{ + T? Value {get;} + [MemberNotNullWhen(true,nameof(Value))] + bool HasValue {get;} +} +// public interface K where F : allows ref struct; +// public interface K : K where TSelf : K, allows ref struct where F : allows ref struct; +// public interface Functor +// where F : Functor, allows ref struct +// { +// // public static abstract K Map(Func f, K thing); +// public static abstract TFunctor Map(Func f, TSource thing) where TFunctor : K, allows ref struct where TSource : K, allows ref struct; +// } +// public static partial class OptionExtensions +// { +// static string hello = "world"; +// struct testttt +// { +// public int gg; +// } +// public static void tt() +// { +// var gg = new testttt(); +// var t = Option.SomeRef(ref gg).Map((ref f) => ref f.gg).Map(f => f.ToString()); +// Func tostring = f=>f.ToString(); +// Option.Map(tostring, t); +// gg.gg = 5; +// var a = Option.Some(5).Map(f => f.ToString()).Map(f => f + f); +// Func tostring = f=>f.Contains('5'); +// var res = Option.Map(a,tostring); +// var opt = RefOption.Some(ref gg); +// var r = opt.MapRef(f => ref f.gg); +// opt.MapRef() +// var s = tostring.Map(opt); +// if (s.HasValue) +// { +// Console.WriteLine(s.Value); +// } +// Option.Some(5).Match( (int f) => f.ToString(), () => "none"); +// Option.None.IfSome((int some) => {}); +// Option.None.Map((some) => some.ToString()); +// Option.None.Bind((some) => Option.Some(some.ToString())); +// int num = 5; +// string num2 = "gg"; +// RefOption.Some(ref num).Match( (int f) => f.ToString(), () => "none"); +// RefOption.None.IfSome((int some) => {}); +// RefOption.None.Map((some) => some.ToString()); +// RefOption.None.Bind((some) => Option.Some(some.ToString())); +// RefOption.None.Map((ref some) => ref hello); +// RefOption.None.Bind((ref some) => RefOption.Some(ref hello)); + +// Option result = Option.Some(5).Map(f => f.ToString()); +// RefOption result2 = RefOption.Some(ref num2); +// // Option res = Option.Map( f => f.Length,result).As(); +// int test(string value)=>value.Length; +// Func factory = test; +// var a = factory.Map(result); +// var aa = result2.MapRef(factory); +// // IOptionCase classOption = new Some("5"); +// // IOptionCase classOption1 = classOption; +// // var result = classOption1 switch +// // { +// // Some some => some.Value, +// // None none => "None", +// // _ => "hello" +// // }; +// } +// // extension(TSource source) where TSource : K +// // { +// // public static TFunctor As(TSource self) where TFunctor : => self; + +// // } +// public static Option As(this K self) => (Option) self; +// // public static K map(Func f, K fa) where F : Functor => F.Map(f,fa); +// public static TFunctor map(Func f, TSource fa) where F : Functor, allows ref struct where TSource : K, allows ref struct where TFunctor : K, allows ref struct => F.Map(f,fa); +// public static Option Map(this Func f, TSource ma) where TSource : K, allows ref struct => map,TSource>(f,ma).As(); +// public static Option Map(this TSource ma, Func f) where TSource : K, allows ref struct => map,TSource>(f,ma).As(); +// // public static RefOption MapRef(this TSource ma, RefOption.RefSelector f) where TSource : K, allows ref struct => map,TSource>(f,ma); +// public static TFunctor Map(this Func f, TSource ma) where TSource : K, allows ref struct where TFunctor : K, allows ref struct => map(f,ma); +// public static TFunctor Map(this TSource ma, Func f) where TSource : K, allows ref struct where TFunctor : K, allows ref struct => map(f,ma); +// // public static TFunctor MapRef(this RefOption.RefSelector f, TSource ma) where TSource : K, allows ref struct where TFunctor : K, allows ref struct => map(f,ma); +// // extension(Func f) +// // { +// // // public static TFunctor Map( TSource ma) where TSource : K, allows ref struct where TFunctor : K, allows ref struct => map(f,ma); + +// // } +// // public static Option Map(this Func f, K> ma) => map>,A,B,Option,(f,ma).As(); +// public static Option ToOption(this T? value) where T : class => value is null ? Option.None : Option.Some(value); +// public static Option ToOption(this T? value) where T : struct => !value.HasValue ? Option.None : Option.Some(value.Value); +// public static void IfSome(this TOption option, Action action) where TOption : struct, IOption, allows ref struct //where T : allows ref struct +// { +// if (option.HasValue) +// { +// action(option.Value); +// } +// } +// // public static IOptionCase ToCase(this IOption option) => option.HasValue ? new Some(option.Value) : None.Of; +// // public static TOptionResult Map(this TOption self, Func f) +// // where TOption : IOption, allows ref struct +// // where TOptionResult : IOption, allows ref struct +// // => self.HasValue ? TOptionResult.Some(f(self.Value)) : TOptionResult.None; +// // public static TOptionResult Bind(this TOption self, Func f) +// // where TOption : IOption, allows ref struct +// // where TOptionResult : IOption, allows ref struct +// // => self.HasValue ? f(self.Value) : TOptionResult.None; + +// public static TResult Match(this TSelf self, Func onSome, Func onNone) where TSelf : IOption, allows ref struct +// => self.HasValue ? onSome(self.Value) : onNone(); +// public static void IfNone(this TOption option, Action action) where TOption : struct, IOption, allows ref struct //where T : allows ref struct +// { +// if (!option.HasValue) +// { +// action(); +// } +// } +// public static TOption GetValueOrNone(this IDictionary dict, TKey key) where TKey : notnull where T : IEquatable where TOption : struct, IOption, allows ref struct +// { +// if (dict.TryGetValue(key, out var result)) +// { +// return TOption.Some(result); +// } +// return TOption.None; +// } +// public static bool TryGetValue(this TOption option,[NotNullWhen(true)] out T? value) where TOption : struct, IOption, allows ref struct +// { +// value = option.Value; +// return false; +// } +// // public static TOption BindAs(this TSelf self) where TSelf : IOption, allows ref struct where TOption : IOption => self.HasValue ? TOption.Some(self.Value) : TOption.None; +// // public static TOption BindAs(this TSelf option, Func f) +// // where TSelf : IOption +// // where TOption : IOption => +// // option.HasValue?f(option.Value) : TOption.None; +// public static T? ToNullable(this IOption option) where T : struct +// { +// if (option.HasValue) +// { +// return option.Value; +// } +// return default; +// } +// public static T Or(this TSelf option, T @default) where TSelf : struct, IOption, allows ref struct => option.HasValue ? option.Value : @default; +// public static T OrDefault(this TSelf option, Func @default) where TSelf : struct, IOption , allows ref struct => option.HasValue ? option.Value : @default(); + +// } +public interface IOption : IOption/*, IEquatable*/ where TSelf : IOption//, allows ref struct +{ + // void Match(Action onSome,Action onNone); + // IOption Bind(Func> f); + // TSelf Map(Func f) where TOptionResult : IOption where TResult : allows ref struct; + // T Or(Func aDefault); + // [MemberNotNullWhen(true,nameof(HasValue))] + // T Value {get;} + public static abstract TSelf Some(T value); + public static abstract TSelf None {get;} + // public TOption BindAs()where TOption : IOption, allows ref struct => HasValue ? TOption.Some(Value) : TOption.None; +} +public class Option //: Functor