Skip to content

Commit

Permalink
feat; re-introduced functional structures to help with upcoming PrcsMngr
Browse files Browse the repository at this point in the history
  • Loading branch information
erikshafer committed Jun 19, 2024
1 parent f41b4b0 commit 0288184
Show file tree
Hide file tree
Showing 4 changed files with 355 additions and 0 deletions.
126 changes: 126 additions & 0 deletions src/Core/Ecommerce.Core/Structures/Either.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
namespace Ecommerce.Core.Structures;

public record Either<TLeft, TRight>
{
public Maybe<TLeft> Left { get; }
public Maybe<TRight> Right { get; }

public Either(TLeft value)
{
Left = Maybe<TLeft>.Of(value);
Right = Maybe<TRight>.Empty;
}

public Either(TRight value)
{
Left = Maybe<TLeft>.Empty;
Right = Maybe<TRight>.Of(value);
}

public Either(Maybe<TLeft> left, Maybe<TRight> right)
{
if (!left.IsPresent && !right.IsPresent)
throw new ArgumentOutOfRangeException(nameof(right));

Left = left;
Right = right;
}

public TMapped Map<TMapped>(
Func<TLeft, TMapped> mapLeft,
Func<TRight, TMapped> mapRight)
{
if (Left.IsPresent)
return mapLeft(Left.GetOrThrow());

if (Right.IsPresent)
return mapRight(Right.GetOrThrow());

throw new Exception("This should not be possible");
}

public void Switch(
Action<TLeft> onLeft,
Action<TRight> onRight)
{
if (Left.IsPresent)
{
onLeft(Left.GetOrThrow());
return;
}

if (Right.IsPresent)
{
onRight(Right.GetOrThrow());
return;
}

throw new Exception("This should not be possible");
}
}

public static class EitherExtensions
{
public static (TLeft? Left, TRight? Right) AssertAnyDefined<TLeft, TRight>(
this (TLeft? Left, TRight? Right) value)
{
if (value.Left == null && value.Right == null)
throw new ArgumentOutOfRangeException(nameof(value), "A value has not been set");

return value;
}

public static TMapped Map<TLeft, TRight, TMapped>(
this (TLeft? Left, TRight? Right) value,
Func<TLeft, TMapped> mapLeft,
Func<TRight, TMapped> mapRight)
where TLeft: struct
where TRight: struct
{
var (left, right) = value.AssertAnyDefined();

if (left.HasValue)
return mapLeft(left.Value);

if (right.HasValue)
return mapRight(right.Value);

throw new Exception("This should not be possible");
}

public static TMapped Map<TLeft, TRight, TMapped>(
this (TLeft? Left, TRight? Right) value,
Func<TLeft, TMapped> mapT1,
Func<TRight, TMapped> mapT2)
{
value.AssertAnyDefined();

var either = value.Left != null
? new Either<TLeft, TRight>(value.Left!)
: new Either<TLeft, TRight>(value.Right!);

return either.Map(mapT1, mapT2);
}

public static void Switch<TLeft, TRight>(
this (TLeft? Left, TRight? Right) value,
Action<TLeft> onT1,
Action<TRight> onT2)
{
value.AssertAnyDefined();

var either = value.Left != null
? new Either<TLeft, TRight>(value.Left!)
: new Either<TLeft, TRight>(value.Right!);

either.Switch(onT1, onT2);
}

public static (TLeft?, TRight?) Either<TLeft, TRight>(
TLeft? left = default)
=> (left, default);

public static (TLeft?, TRight?) Either<TLeft, TRight>(
TRight? right = default)
=> (default, right);
}
44 changes: 44 additions & 0 deletions src/Core/Ecommerce.Core/Structures/Maybe.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace Ecommerce.Core.Structures;

public record Maybe<TSomething>
{
private readonly TSomething? _value;
public bool IsPresent { get; }

private Maybe(TSomething value, bool isPresent)
{
_value = value;
IsPresent = isPresent;
}

public static readonly Maybe<TSomething> Empty = new(default!, false);

public static Maybe<TSomething> Of(TSomething value) =>
value != null ? new Maybe<TSomething>(value, true) : Empty;

public static Maybe<TSomething> If(bool check, Func<TSomething> getValue) =>
check ? new Maybe<TSomething>(getValue(), true) : Empty;

public TSomething GetOrThrow() =>
IsPresent ? _value! : throw new ArgumentNullException(nameof(_value));

public TSomething GetOrDefault(TSomething defaultValue = default!) =>
IsPresent ? _value ?? defaultValue : defaultValue;

public void IfExists(Action<TSomething> perform)
{
if (IsPresent)
{
perform(_value!);
}
}
}

public static class Maybe
{
public static Maybe<TSomething> Of<TSomething>(TSomething value) =>
Maybe<TSomething>.Of(value);

public static Maybe<TSomething> If<TSomething>(bool check, Func<TSomething> getValue) =>
Maybe<TSomething>.If(check, getValue);
}
106 changes: 106 additions & 0 deletions src/Core/Ecommerce.Core/Structures/OneOf.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
namespace Ecommerce.Core.Structures;

public record OneOf<T1, T2, T3>
{
public Maybe<T1> First { get; }
public Maybe<T2> Second { get; }
public Maybe<T3> Third { get; }

public OneOf(T1 value)
{
First = Maybe<T1>.Of(value);
Second = Maybe<T2>.Empty;
Third = Maybe<T3>.Empty;
}

public OneOf(T2 value)
{
First = Maybe<T1>.Empty;
Second = Maybe<T2>.Of(value);
Third = Maybe<T3>.Empty;
}

public OneOf(T3 value)
{
First = Maybe<T1>.Empty;
Second = Maybe<T2>.Empty;
Third = Maybe<T3>.Of(value);
}

public OneOf((T1? First, T2? Second, T3? Third) value)
{
First = value.First != null ? Maybe<T1>.Of(value.First) : Maybe<T1>.Empty;
Second = value.Second != null ? Maybe<T2>.Of(value.Second) : Maybe<T2>.Empty;
Third = value.Third != null ? Maybe<T3>.Of(value.Third) : Maybe<T3>.Empty;
}

public TMapped Map<TMapped>(
Func<T1, TMapped> mapT1,
Func<T2, TMapped> mapT2,
Func<T3, TMapped> mapT3)
{
if (First.IsPresent)
{
return mapT1(First.GetOrThrow());
}

if (Second.IsPresent)
{
return mapT2(Second.GetOrThrow());
}

if (Third.IsPresent)
{
return mapT3(Third.GetOrThrow());
}

throw new Exception("This should not be possible");
}

public void Switch(
Action<T1> onT1,
Action<T2> onT2,
Action<T3> onT3)
{
if (First.IsPresent)
{
onT1(First.GetOrThrow());
return;
}

if (Second.IsPresent)
{
onT2(Second.GetOrThrow());
return;
}

if (Third.IsPresent)
{
onT3(Third.GetOrThrow());
return;
}

throw new Exception("This should not be possible");
}
}

public static class OneOfExtensions
{
public static void Map<T1, T2, T3, TMapped>(
this (T1? First, T2? Second, T3? Third) value,
Func<T1, TMapped> mapT1,
Func<T2, TMapped> mapT2,
Func<T3, TMapped> mapT3)
{
new OneOf<T1, T2, T3>(value).Map(mapT1, mapT2, mapT3);
}

public static void Switch<T1, T2, T3, TMapped>(
this (T1? First, T2? Second, T3? Third) value,
Action<T1> onT1,
Action<T2> onT2,
Action<T3> onT3)
{
new OneOf<T1, T2, T3>(value).Switch(onT1, onT2, onT3);
}
}
79 changes: 79 additions & 0 deletions src/Core/Ecommerce.Core/Structures/Result.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
namespace Ecommerce.Core.Structures;

public record Result<TSuccess, TError>
{
public Maybe<TSuccess> Success { get; }
public Maybe<TError> Error { get; }

public Result(TSuccess value)
{
Success = Maybe<TSuccess>.Of(value);
Error = Maybe<TError>.Empty;
}

public Result(TError value)
{
Success = Maybe<TSuccess>.Empty;
Error = Maybe<TError>.Of(value);
}

public Result(Maybe<TSuccess> success, Maybe<TError> error)
{
if (!success.IsPresent && !error.IsPresent)
throw new ArgumentOutOfRangeException(nameof(error));

Success = success;
Error = error;
}

public TMapped Map<TMapped>(
Func<TSuccess, TMapped> mapSuccess,
Func<TError, TMapped> mapFailure
)
{
if (Success.IsPresent)
return mapSuccess(Success.GetOrThrow());

if (Error.IsPresent)
return mapFailure(Error.GetOrThrow());

throw new Exception("That should never happen!");
}

public object FlatMap()
{
if (Success.IsPresent)
return Success.GetOrThrow()!;

if (Error.IsPresent)
return Error.GetOrThrow()!;

throw new Exception("That should never happen!");
}

public void Switch(
Action<TSuccess> onSuccess,
Action<TError> onFailure)
{
if (Success.IsPresent)
{
onSuccess(Success.GetOrThrow());
return;
}

if (Error.IsPresent)
{
onFailure(Error.GetOrThrow());
return;
}

throw new Exception("That should never happen!");
}
}

public static class Result
{
public static Result<TSuccess, TError> Success<TSuccess, TError>(TSuccess success) => new(success);

public static Result<TSuccess, TError> Failure<TSuccess, TError>(TError error) => new(error);
}

0 comments on commit 0288184

Please sign in to comment.