Skip to content

Commit

Permalink
interfaces removed, all types implemting iequatable
Browse files Browse the repository at this point in the history
  • Loading branch information
pimbrouwers committed Aug 26, 2024
1 parent ef45583 commit 8ade153
Show file tree
Hide file tree
Showing 15 changed files with 353 additions and 249 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var optionNone = Option<int>.None();
Options are commonly used when a operation might not return a value.

```csharp
public IOption<int> TryFind(IEnumerable<int> numbers, Func<int, bool> predicate) =>
public Option<int> TryFind(IEnumerable<int> numbers, Func<int, bool> predicate) =>
numbers.FirstOrDefault(predicate).ToOption();
```

Expand Down
69 changes: 36 additions & 33 deletions src/Danom/Option/Option.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,18 @@ namespace Danom;
/// using nulls, and allows for more expressive code with exhaustive matching.
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IOption<T>
{
bool IsSome { get; }
bool IsNone { get; }
U Match<U>(Func<T, U> some, Func<U> none);
IOption<U> Bind<U>(Func<T, IOption<U>> bind);
IOption<U> Map<U>(Func<T, U> map);
T DefaultValue(T defaultValue);
T DefaultWith(Func<T> defaultWith);
IOption<T> OrElse(IOption<T> ifNone);
IOption<T> OrElseWith(Func<IOption<T>> ifNoneWith);
}

/// <inheritdoc cref="IOption{T}" />
public readonly struct Option<T>
: IOption<T>
: IEquatable<Option<T>>
{
private readonly T? _some = default;

private Option(T t)
{
_some = t;
IsSome = true;
if (t is not null)
{
_some = t;
IsSome = true;
}
}

/// <summary>
Expand Down Expand Up @@ -58,8 +47,8 @@ public U Match<U>(Func<T, U> some, Func<U> none) =>
/// <typeparam name="U"></typeparam>
/// <param name="bind"></param>
/// <returns></returns>
public IOption<U> Bind<U>(
Func<T, IOption<U>> bind) =>
public Option<U> Bind<U>(
Func<T, Option<U>> bind) =>
Match(bind, Option<U>.None);

/// <summary>
Expand All @@ -68,7 +57,7 @@ public IOption<U> Bind<U>(
/// <typeparam name="U"></typeparam>
/// <param name="map"></param>
/// <returns></returns>
public IOption<U> Map<U>(
public Option<U> Map<U>(
Func<T, U> map) =>
Bind(x => Option<U>.Some(map(x)));

Expand All @@ -95,17 +84,17 @@ public T DefaultWith(
/// </summary>
/// <param name="ifNone"></param>
/// <returns></returns>
public IOption<T> OrElse(
IOption<T> ifNone) =>
public Option<T> OrElse(
Option<T> ifNone) =>
Match(Option<T>.Some, () => ifNone);

/// <summary>
/// Return Option if it is Some, otherwise evaluate ifNoneWith.
/// </summary>
/// <param name="ifNoneWith"></param>
/// <returns></returns>
public IOption<T> OrElseWith(
Func<IOption<T>> ifNoneWith) =>
public Option<T> OrElseWith(
Func<Option<T>> ifNoneWith) =>
Match(Option<T>.Some, ifNoneWith);


Expand All @@ -114,37 +103,37 @@ public IOption<T> OrElseWith(
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static IOption<T> Some(T value) =>
public static Option<T> Some(T value) =>
new Option<T>(value);

/// <summary>
/// Creates Option with the specified value wrapped in a completed Task.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static Task<IOption<T>> SomeAsync(T value) =>
public static Task<Option<T>> SomeAsync(T value) =>
Task.FromResult(Some(value));

/// <summary>
/// Creates a new Option with the value of the awaited Task.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static async Task<IOption<T>> SomeAsync(Task<T> value) =>
public static async Task<Option<T>> SomeAsync(Task<T> value) =>
Some(await value);

/// <summary>
/// Creates a new Option with no value.
/// </summary>
/// <returns></returns>
public static IOption<T> None() =>
public static Option<T> None() =>
new Option<T>();

/// <summary>
/// Creates a new Option with no value wrapped in a completed Task.
/// </summary>
/// <returns></returns>
public static Task<IOption<T>> NoneAsync() =>
public static Task<Option<T>> NoneAsync() =>
Task.FromResult(None());

public static bool operator ==(Option<T> left, Option<T> right) =>
Expand All @@ -154,10 +143,24 @@ public static Task<IOption<T>> NoneAsync() =>
!(left == right);

public override bool Equals(object? obj) =>
_some is not null && obj is not null && _some.Equals(obj);
obj is Option<T> o && Equals(o);

public override int GetHashCode()
=> _some is null ? 0 : _some.GetHashCode();
public readonly bool Equals(Option<T> other) =>
Match(
some: x1 =>
other.Match(
some: x2 => x1 is not null && x2 is not null && x2.Equals(x1),
none: () => false),
none: () =>
other.Match(
some: _ => false,
none: () => true)
);

public override int GetHashCode() =>
Match(
some: x => x is null ? 0 : x.GetHashCode(),
none: () => 0);

public override string ToString() =>
Match(
Expand All @@ -178,7 +181,7 @@ public static class OptionActionExtensions
/// <param name="option"></param>
/// <param name="some"></param>
/// <param name="none"></param>
public static void Match<T>(this IOption<T> option, Action<T> some, Action none)
public static void Match<T>(this Option<T> option, Action<T> some, Action none)
{
if (option.ToNullable() is T t)
{
Expand Down
34 changes: 17 additions & 17 deletions src/Danom/Option/OptionNullable.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Danom;

/// <summary>
/// Contains extension methods for <see cref="IOption{T}"/> that allow for
/// Contains extension methods for <see cref="Option{T}"/> that allow for
/// converting between nullable types and options.
/// </summary>
public static class OptionNullableExtensions
Expand All @@ -12,7 +12,7 @@ public static class OptionNullableExtensions
/// <typeparam name="T"></typeparam>
/// <param name="x"></param>
/// <returns></returns>
public static IOption<T> ToOption<T>(this T? x) =>
public static Option<T> ToOption<T>(this T? x) =>
x is not null && (!Equals(x, default(T))) ? Option<T>.Some(x) : Option<T>.None();

/// <summary>
Expand All @@ -21,15 +21,15 @@ public static IOption<T> ToOption<T>(this T? x) =>
/// <typeparam name="T"></typeparam>
/// <param name="x"></param>
/// <returns></returns>
public static IOption<T> ToOption<T>(this T? x) where T : struct =>
public static Option<T> ToOption<T>(this T? x) where T : struct =>
(x is T t) ? Option<T>.Some(t) : Option<T>.None();

/// <summary>
/// Converts a nullable string to an option.
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
public static IOption<string> ToOption(this string? x) =>
public static Option<string> ToOption(this string? x) =>
x is not null && !string.IsNullOrWhiteSpace(x) ? Option<string>.Some(x) : Option<string>.None();

/// <summary>
Expand All @@ -38,102 +38,102 @@ public static IOption<string> ToOption(this string? x) =>
/// <typeparam name="T"></typeparam>
/// <param name="option"></param>
/// <returns></returns>
public static T? ToNullable<T>(this IOption<T> option) =>
public static T? ToNullable<T>(this Option<T> option) =>
option.Match(some: x => x, none: () => default!);

/// <summary>
/// Converts a char option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static char? ToNullable(this IOption<char> option) =>
public static char? ToNullable(this Option<char> option) =>
option.Match(x => new char?(x), () => null);

/// <summary>
/// Converts a bool option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static bool? ToNullable(this IOption<bool> option) =>
public static bool? ToNullable(this Option<bool> option) =>
option.Match(x => new bool?(x), () => null);

/// <summary>
/// Converts a byte option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static byte? ToNullable(this IOption<byte> option) =>
public static byte? ToNullable(this Option<byte> option) =>
option.Match(x => new byte?(x), () => null);

/// <summary>
/// Converts a short option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static short? ToNullable(this IOption<short> option) =>
public static short? ToNullable(this Option<short> option) =>
option.Match(x => new short?(x), () => null);

/// <summary>
/// Converts a ushort option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static int? ToNullable(this IOption<int> option) =>
public static int? ToNullable(this Option<int> option) =>
option.Match(x => new int?(x), () => null);

/// <summary>
/// Converts a uint option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>/
public static long? ToNullable(this IOption<long> option) =>
public static long? ToNullable(this Option<long> option) =>
option.Match(x => new long?(x), () => null);

/// <summary>
/// Converts a ulong option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static decimal? ToNullable(this IOption<decimal> option) =>
public static decimal? ToNullable(this Option<decimal> option) =>
option.Match(x => new decimal?(x), () => null);

/// <summary>
/// Converts a double option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static double? ToNullable(this IOption<double> option) =>
public static double? ToNullable(this Option<double> option) =>
option.Match(x => new double?(x), () => null);

/// <summary>
/// Converts a float option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static float? ToNullable(this IOption<float> option) =>
public static float? ToNullable(this Option<float> option) =>
option.Match(x => new float?(x), () => null);

/// <summary>
/// Converts a decimal option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static Guid? ToNullable(this IOption<Guid> option) =>
public static Guid? ToNullable(this Option<Guid> option) =>
option.Match(x => new Guid?(x), () => null);

/// <summary>
/// Converts a DateTime option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static DateTime? ToNullable(this IOption<DateTime> option) =>
public static DateTime? ToNullable(this Option<DateTime> option) =>
option.Match(x => new DateTime?(x), () => null);

/// <summary>
/// Converts a DateTimeOffset option to a nullable value.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public static DateOnly? ToNullable(this IOption<DateOnly> option) =>
public static DateOnly? ToNullable(this Option<DateOnly> option) =>
option.Match(x => new DateOnly?(x), () => null);
}
Loading

0 comments on commit 8ade153

Please sign in to comment.