调整Option

This commit is contained in:
sanchime 2023-01-09 21:12:00 +08:00
parent 59f4e8c110
commit 8b4aae1d63
25 changed files with 173 additions and 166 deletions

13
.idea/.idea.Sanchime/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/contentModel.xml
/projectSettingsUpdater.xml
/modules.xml
/.idea.Sanchime.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1 @@
Sanchime

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -6,5 +6,5 @@ namespace Sanchime.Functional.Extensions;
public static class DictionaryExtension
{
public static Option<T> Lookup<K, T>(this Dictionary<K, T> dictionary, K key) where K: notnull
=> dictionary.TryGetValue(key, out T? value) ? Option.Some(value) : Option.None;
=> dictionary.TryGetValue(key, out T? value) ? Optional.Some(value) : Optional.None;
}

View File

@ -3,8 +3,8 @@ namespace Sanchime.Functional.Extensions;
public static class EnumExtension
{
/// <summary>
/// 将字符串转换为相应的枚举值的<see cref="Option"/
/// 将字符串转换为相应的枚举值的<see cref="Optional"/
/// </summary>
public static Option<T> Parse<T>(this string @this) where T : struct
=> Enum.TryParse(@this, out T val) ? Option.Some(val) : Option.None;
=> Enum.TryParse(@this, out T val) ? Optional.Some(val) : Optional.None;
}

View File

@ -11,7 +11,7 @@ public static class IEnumerableExtension
public static Func<T, IEnumerable<T>> Return<T>() => t => List(t);
/// <summary>
/// 将序列内第一条元素转换为<see cref="Option"/>
/// 将序列内第一条元素转换为<see cref="Optional"/>
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
@ -20,14 +20,14 @@ public static class IEnumerableExtension
{
if (source is null)
{
return Option.None;
return Optional.None;
}
var first = source.FirstOrDefault();
return first is not null ? Option.Some(first) : Option.None;
return first is not null ? Optional.Some(first) : Optional.None;
}
/// <summary>
/// 给定谓词,查询序列内第一条数据并将其转换为<see cref="Option"/>
/// 给定谓词,查询序列内第一条数据并将其转换为<see cref="Optional"/>
/// </summary>
/// <param name="source">数据源</param>
/// <param name="predicate">谓词</param>
@ -37,14 +37,14 @@ public static class IEnumerableExtension
{
if (source is null)
{
return Option.None;
return Optional.None;
}
var first = source.Where(predicate).FirstOrDefault();
return first is not null ? Option.Some(first) : Option.None;
return first is not null ? Optional.Some(first) : Optional.None;
}
/// <summary>
/// 将序列内最后一个元素转换为<see cref="Option"/>
/// 将序列内最后一个元素转换为<see cref="Optional"/>
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
@ -53,14 +53,14 @@ public static class IEnumerableExtension
{
if (source is null)
{
return Option.None;
return Optional.None;
}
var tail = source.LastOrDefault();
return tail is not null ? Option.Some(tail) : Option.None;
return tail is not null ? Optional.Some(tail) : Optional.None;
}
/// <summary>
/// 给定谓词,查询序列内最后一条数据并将其转换为<see cref="Option"/>
/// 给定谓词,查询序列内最后一条数据并将其转换为<see cref="Optional"/>
/// </summary>
/// <param name="source">数据源</param>
/// <param name="predicate">谓词</param>
@ -70,10 +70,10 @@ public static class IEnumerableExtension
{
if (source is null)
{
return Option.None;
return Optional.None;
}
var last = source.Where(predicate).LastOrDefault();
return last is not null ? Option.Some(last) : Option.None;
return last is not null ? Optional.Some(last) : Optional.None;
}
public static R Match<T, R>(this IEnumerable<T> source, Func<R> Empty, Func<T, IEnumerable<T>, R> Otherwise)

View File

@ -3,8 +3,8 @@ namespace Sanchime.Functional.Extensions;
public static class NullableExtension
{
/// <summary>
/// 将<see cref="Nullable"/>值转换为<see cref="Option"/>
/// 将<see cref="Nullable"/>值转换为<see cref="Optional"/>
/// </summary>
public static Option<T> ToOption<T>(this Nullable<T> @this) where T : struct
=> @this.HasValue ? Option.Some(@this.Value) : Option.None;
=> @this.HasValue ? Optional.Some(@this.Value) : Optional.None;
}

View File

@ -2,8 +2,7 @@ namespace Sanchime.Functional.Extensions;
public static class OptionExtension
{
public static Unit Match<T>(this Option<T> option, Action None, Action<T> Some)
=> option.Match(None.ToFunc(), Some.ToFunc());
#region :
@ -13,27 +12,18 @@ public static class OptionExtension
// public static Option<R> Map<T, R>(this Some<T> some, Func<T, R> mapping)
// => Option.Some(mapping(some.Value));
public static Option<R> Map<T, R>(this Option<T> option, Func<T, R> mapping)
=> option.Match(() => Option.None, (val) => Option.Some(mapping(val)));
// public static Option<R> Map<T, R>(this Option<T> option, Func<T, R> mapping)
// => option.Match(() => Option.None, (val) => Option.Some(mapping(val)));
// public static Option<Func<T2, R>> Map<T1, T2, R>(this Option<T1> option, Func<T1, T2, R> mapping)
// public static IOption Map<T1, T2, R>(this Option<T1> option, Func<T1, T2, R> mapping)
// => option.Map(mapping.Curry());
// public static Option<Func<T2, T3, R>> Map<T1, T2, T3, R>(this Option<T1> option, Func<T1, T2, T3, R> mapping)
// public static IOption Map<T1, T2, T3, R>(this Option<T1> option, Func<T1, T2, T3, R> mapping)
// => option.Map(mapping.CurryFirst());
#endregion
#region :
/// <summary>
/// Return自然变换: 将上下文无关的值导入至上下文有关的世界
/// </summary>
/// <param name="value"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Option<T> Return<T>(T value)
=> value is null ? Option.None : Option.Some(value);
public static Option<R> Bind<T, R>(this Option<T> option, Func<T, Option<R>> binding)
=> option.Match(() => new Option<R>(), binding);
@ -46,12 +36,12 @@ public static class OptionExtension
// #region 应用函子
// public static Option<R> Apply<T, R>(this Option<Func<T, R>> option, Option<T> value)
// => option.Match(
// None: () => Option.None,
// Some: (apply) => value.Match(
// None: () => Option.None,
// Some: (val) => Option.Some(apply(val))));
public static Option<R> Apply<T, R>(this Option<Func<T, R>> option, Option<T> value)
=> option.Match(
None: () => Optional.None,
Some: (apply) => value.Match(
None: () => Optional.None,
Some: (val) => Optional.Some(apply(val))));
// public static Option<Func<T2, R>> Apply<T1, T2, R>(this Option<Func<T1, T2, R>> option, Option<T1> value)
// => option.Map(CurryingExtension.Curry).Apply(value);
@ -95,7 +85,7 @@ public static class OptionExtension
=> left.Match(() => right(), (_) => left);
/// <summary>
/// 将<see cref="Option"转为<see cref="Either"/>
/// 将<see cref="Optional"转为<see cref="Either"/>
/// </summary>
/// <param name="option"></param>
/// <param name="error"></param>
@ -111,18 +101,18 @@ public static class OptionExtension
#region Linq式
// public static Option<R> Select<T, R>(this Option<T> source, Func<T, R> mapping)
// => source.Map(mapping);
public static IOption Select<T, R>(this Option<T> source, Func<T, R> mapping)
=> source.Map(mapping);
public static Option<T> Where<T, R>(this Option<T> source, Func<T, bool> predicate)
=> source.Match(() => Option.None, (val) => predicate(val) ? source : Option.None);
=> source.Match(() => Optional.None, (val) => predicate(val) ? source : Optional.None);
public static Option<RR> SelectMany<T, R, RR>(this Option<T> source, Func<T, Option<R>> binding, Func<T, R, RR> project)
=> source.Match(
None: () => Option.None,
None: () => Optional.None,
Some: (val) => binding(val).Match(
None: () => Option.None,
Some: (res) => Option.Some(project(val, res))));
None: () => Optional.None,
Some: (res) => Optional.Some(project(val, res))));
#endregion

View File

@ -10,7 +10,7 @@ public interface IEither<TLeft, TRight> : IEither
}
public interface IEither : IFunctor
public interface IEither
{
}

View File

@ -1,16 +0,0 @@
namespace Sanchime.Functional.Products;
/// <summary>
/// 应用函子
/// </summary>
/// <typeparam name="TCategory"></typeparam>
public interface IApplicative<TCategory> : IApplicative, IFunctor<TCategory>
where TCategory : ICategory
{
}
public interface IApplicative : IFunctor
{
}

View File

@ -1,6 +0,0 @@
namespace Sanchime.Functional.Products;
public interface ICategory
{
}

View File

@ -1,24 +0,0 @@
namespace Sanchime.Functional.Products;
/// <summary>
/// 函子
/// 同一范畴内不同对象之间的映射
/// </summary>
/// <typeparam name="TCategory"></typeparam>
public interface IFunctor<TCategory, TValue> : IFunctor
where TCategory : ICategory
{
TCategory Map<TResult>(Func<TValue, TResult> mapping);
}
public interface IFunctor<TCategory> : IFunctor
where TCategory : ICategory
{
TCategory Map<TValue, TResult>(Func<TValue, TResult> mapping);
}
public interface IFunctor : ICategory
{
public TResult Map<TResult>(Func<ICategory, TResult> mapping)
=> mapping(this);
}

View File

@ -1,10 +0,0 @@
namespace Sanchime.Functional.Products;
/// <summary>
/// 单子
/// </summary>
/// <typeparam name="TCategory"></typeparam>
public interface IMonad<TCategory> : IApplicative<TCategory>
where TCategory : ICategory
{
}

View File

@ -5,12 +5,16 @@ using System.Threading.Tasks;
namespace Sanchime.Functional.Products;
public interface IOption : IFunctor
public interface IOption
{
bool IsSome { get; }
}
public interface IOption<TValue> : IOption, IFunctor<IOption, TValue>
/// <summary>
/// 选项
/// </summary>
/// <typeparam name="TValue">选项内部维护的值</typeparam>
public interface IOption<out TValue> : IOption
{
TValue Value { get; }
}

View File

@ -1,10 +1,14 @@
namespace Sanchime.Functional.Products;
/// <summary>
/// <see cref="Option"/>的<see cref="None"/>状态
/// <see cref="Optional"/>的<see cref="None"/>状态
/// </summary>
public readonly partial struct None : IOption
{
/// <summary>
/// 静态构造形成单例
/// </summary>
static None() { }
public bool IsSome => false;
internal static readonly None Default = new();
}

View File

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sanchime.Functional.Products;
public readonly partial struct None : IFunctor<None>
{
public None Map<TValue, TResult>(Func<TValue, TResult> mapping)
=> Option.None;
}

View File

@ -1,6 +1,8 @@
using Sanchime.Functional.Extensions;
namespace Sanchime.Functional.Products;
public static class Option
public static class Optional
{
public static None None => None.Default;
public static Option<TValue> Some<TValue>(TValue value) => new Some<TValue>(value);
@ -18,39 +20,39 @@ public readonly partial struct Option<TValue> : IOption<TValue>, IEquatable<None
=> (_isSome, _value) = (true, value ?? throw new ArgumentNullException(nameof(value)));
/// <summary>
/// 将<see cref="None"/>转换为<see cref="Option"/>
/// 将<see cref="None"/>转换为<see cref="Optional"/>
/// </summary>
/// <param name="_"></param>
public static implicit operator Option<TValue>(None _)
=> new();
/// <summary>
/// 将<see cref="Some"/>转换为<see cref="Option"/>
/// 将<see cref="Some"/>转换为<see cref="Optional"/>
/// </summary>
/// <param name="some"></param>
public static implicit operator Option<TValue>(Some<TValue> some)
=> new(some.Value);
/// <summary>
/// 将常规值提升至<see cref="Option"/>
/// 将常规值提升至<see cref="Optional"/>
/// </summary>
/// <param name="value"></param>
public static implicit operator Option<TValue>(TValue value)
=> value is null ? Option.None : Option.Some(value);
public bool IsSome => this.Match(() => false, (_) => true);
=> value is null ? Optional.None : Optional.Some(value);
public bool IsSome => _isSome;
/// <summary>
/// 获取<see cref="Option"/>内部的值,如果为<see cref="Option.None"/>则抛出异常
/// 获取<see cref="Optional"/>内部的值,如果为<see cref="Optional.None"/>则抛出异常
/// </summary>
/// <returns></returns>
public TValue ValueUnsafe => this.Match(() => throw new InvalidOperationException(), val => val);
/// <summary>
/// 获取<see cref="Option"/>内部的值,如果为<see cref="Option.None"/>则返回默认值
/// 获取<see cref="Optional"/>内部的值,如果为<see cref="Optional.None"/>则返回默认值
/// </summary>
/// <returns></returns>
public TValue Value => this.Match(() => default!, val => val);
public TValue Value => _isNone ? default! : _value;
/// <summary>
/// 转换为序列
@ -60,15 +62,7 @@ public readonly partial struct Option<TValue> : IOption<TValue>, IEquatable<None
{
if (_isSome) yield return _value;
}
/// <summary>
/// 接收两个函数,根据<see cref="Option"/>的状态进行求值
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="None">None状态</param>
/// <param name="Some">Some状态</param>
/// <returns></returns>
public TResult Match<TResult>(Func<TResult> None, Func<TValue, TResult> Some)
=> _isSome ? Some(_value) : None();
public bool Equals(Option<TValue> other)
=> this._isSome == other._isSome

View File

@ -2,18 +2,39 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Sanchime.Functional.Extensions;
namespace Sanchime.Functional.Products;
public readonly partial struct Option<TValue>
public static class OptionFunctor
{
/// <summary>
/// 接收两个函数,根据<see cref="Optional"/>的状态进行求值
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="None">None状态</param>
/// <param name="Some">Some状态</param>
/// <returns></returns>
public static TResult Match<TValue, TResult>(this IOption<TValue> option, Func<TResult> None, Func<TValue, TResult> Some)
=> option.IsSome ? Some(option.Value) : None();
public static Unit Match<TValue>(this IOption<TValue> option, Action None, Action<TValue> Some)
=> option.Match(None.ToFunc(), Some.ToFunc());
/// <summary>
/// 映射
/// </summary>
/// <param name="option"></param>
/// <param name="mapping"></param>
/// <typeparam name="TValue"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <returns></returns>
public IOption Map<TResult>(Func<TValue, TResult> mapping)
=> this.Match(() => Option.None, value => Option.Some(mapping(value)));
public static IOption<TResult> Map<TValue, TResult>(this IOption<TValue> option, Func<TValue, TResult> mapping)
=> option.Match(() => Optional.None, value => Optional.Some(mapping(value)));
public static IOption<Func<TValue1, TResult>> Map<TValue, TValue1, TResult>(this IOption<TValue> option,Func<TValue, TValue1, TResult> mapping)
=> option.Map(mapping.Curry());
public static IOption<Func<TValue1, TValue2, TResult>> Map<TValue, TValue1, TValue2, TResult>(this IOption<TValue> option, Func<TValue, TValue1, TValue2, TResult> mapping)
=> option.Map(mapping.CurryFirst());
}

View File

@ -0,0 +1,16 @@
namespace Sanchime.Functional.Products;
public readonly partial struct Option<TValue>
{
/// <summary>
/// Return自然变换: 将上下文无关的值导入至上下文有关的世界
/// </summary>
/// <param name="value"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IOption Return<T>(T value)
=> value is null ? Optional.None : Optional.Some(value);
public static IOption Return(TValue value)
=> Return<TValue>(value);
}

View File

@ -1,16 +1,15 @@
namespace Sanchime.Functional.Products;
/// <summary>
/// <see cref="Option"/>的<see cref="Some"/>状态
/// <see cref="Optional"/>的<see cref="Some"/>状态
/// </summary>
/// <typeparam name="TValue"></typeparam>
public readonly struct Some<TValue> : IOption<TValue>
public readonly struct Some<TValue> : IOption
{
public bool IsSome => Value is not null;
public TValue Value { get; }
internal Some(TValue value) => Value = value ?? throw new ArgumentNullException(nameof(value), "不能在Some状态中包装null值, 应该使用None");
public IOption Map<TResult>(Func<TValue, TResult> mapping)
=> Option.Some(mapping(this.Value));
=> Optional.Some(mapping(this.Value));
}

View File

@ -3,38 +3,51 @@ using Sanchime.Functional.Extensions;
using Sanchime.Toolkits;
void foo(Option<int> option)
try
{
var res = option.Map(x => x + 2);
res.WriteLine();
}
"预计打印Some(12)".WriteLine();
foo(10);
"预计打印None".WriteLine();
foo(Option.None);
void foo(Option<int> option)
{
var res = option.Map(x => x + 2);
res.WriteLine();
}
"预计打印Char".WriteLine();
Option.Some(1)
.Map(x => (float)x + 1.2)
.Map(x => x.ToString())
.Map(x => x.GetType().Name)
.ForEach(x => x.WriteLine());
"预计打印Some(12)".WriteLine();
foo(10);
"预计打印None".WriteLine();
foo(Optional.None);
"预计打印Some(String)".WriteLine();
Optional.Some(1)
.Map(x => x + 1.2)
.Map(x => x.ToString())
.Map(x => x.GetType().Name)
.WriteLine();
// 测试Option的Bind
var parse = (string s) => Int32.TryParse(s, out int i) ? Option.Some(i) : Option.None;
var foo1 = (string s) => s.Pipe(parse).Bind(Age.Of);
foo1("111").WriteLine();
foo1("aaa").WriteLine();
foo1("123").WriteLine();
var parse = (string s) => Int32.TryParse(s, out int i) ? Optional.Some(i) : Optional.None;
var foo1 = (string s) => s.Pipe(parse).Bind(Age.Of);
"预计打印Some(111)".WriteLine();
foo1("111").WriteLine();
"预计打印None".WriteLine();
foo1("aaa").WriteLine();
"预计打印Some(123)".WriteLine();
foo1("123").WriteLine();
// 管道
foo1("1ab").Pipe(x => Console.WriteLine(x));
"预计打印None".WriteLine();
foo1("1ab").Pipe(x => x.WriteLine());
}
catch (Exception ex)
{
ex.Message.WriteLine();
ex.StackTrace.WriteLine();
}
public struct Age
{
private int _value;
public static Option<Age> Of(int age)
=> IsValid(age) ? Option.Some(new Age(age)) : Option.None;
=> IsValid(age) ? Optional.Some(new Age(age)) : Optional.None;
private Age(int age)
{

View File

@ -5,7 +5,7 @@ public static class Basic
{
public static void WriteLine<T>(this T @this)
{
Debug.WriteLine(@this);
Console.WriteLine(@this);
}
public static void WriteLine<T>(this T @this, Func<T, T> func)

View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sanchime/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>