Skip to content

Commit

Permalink
#62 - Added pattern matching to Either type.
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidArno committed Sep 4, 2019
1 parent b1e7db0 commit 1308836
Show file tree
Hide file tree
Showing 7 changed files with 487 additions and 1 deletion.
9 changes: 8 additions & 1 deletion src/SuccincT/Unions/Either.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using SuccincT.Functional;
using System;
using SuccincT.Options;
using SuccincT.Unions.PatternMatchers;

namespace SuccincT.Unions
{
Expand Down Expand Up @@ -40,6 +42,11 @@ public Either(TRight right)

public Option<TRight> TryRight => _tryRight.GetValue(_isRight, _right);

public IEitherFuncPatternMatcher<TLeft, TRight, TResult> Match<TResult>()
=> new EitherPatternMatcher<TLeft, TRight, TResult>(this);

public IEitherActionPatternMatcher<TLeft, TRight> Match() => new EitherPatternMatcher<TLeft, TRight, Unit>(this);

public override bool Equals(object obj) => obj is Either<TLeft,TRight> either && EithersEqual(this, either);

public override int GetHashCode()
Expand Down
87 changes: 87 additions & 0 deletions src/SuccincT/Unions/PatternMatchers/EitherPatternMatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using SuccincT.Functional;
using static SuccincT.Functional.TypedLambdas;

namespace SuccincT.Unions.PatternMatchers
{
internal sealed class EitherPatternMatcher<TLeft, TRight, TResult> :
IEitherFuncPatternMatcher<TLeft, TRight, TResult>,
IEitherActionPatternMatcher<TLeft, TRight>,
IUnionFuncPatternMatcherAfterElse<TResult>,
IUnionActionPatternMatcherAfterElse
{
private readonly Either<TLeft, TRight> _either;

private readonly MatchSelectorsForEither<TLeft, TRight, TResult> _selector =
MatchSelectorsCreator.CreateEitherSelectors<TLeft, TRight, TResult>();

internal EitherPatternMatcher(Either<TLeft, TRight> either) => _either = either;

IUnionFuncPatternCaseHandler<IEitherFuncPatternMatcher<TLeft, TRight, TResult>, TLeft, TResult>
IEitherFuncPatternMatcher<TLeft, TRight, TResult>.Left()
{
return new UnionPatternCaseHandler<IEitherFuncPatternMatcher<TLeft, TRight, TResult>, TLeft, TResult>(
_selector.RecordAction,
this);
}

IUnionFuncPatternCaseHandler<IEitherFuncPatternMatcher<TLeft, TRight, TResult>, TRight, TResult>
IEitherFuncPatternMatcher<TLeft, TRight, TResult>.Right()
{
return new UnionPatternCaseHandler<IEitherFuncPatternMatcher<TLeft, TRight, TResult>, TRight, TResult>(
_selector.RecordAction,
this);
}

IUnionFuncPatternMatcherAfterElse<TResult> IEitherFuncPatternMatcher<TLeft, TRight, TResult>.Else(
Func<Either<TLeft, TRight>, TResult> elseFunc)
{
_selector.RecordElseFunction(elseFunc);
return this;
}

IUnionFuncPatternMatcherAfterElse<TResult> IEitherFuncPatternMatcher<TLeft, TRight, TResult>.Else(
TResult elseValue)
{
_selector.RecordElseFunction(Func((Either<TLeft, TRight> _) => elseValue));
return this;
}

TResult IEitherFuncPatternMatcher<TLeft, TRight, TResult>.Result() => _selector.ResultNoElse(_either);

TResult IUnionFuncPatternMatcherAfterElse<TResult>.Result() => _selector.ResultUsingElse(_either);

IUnionActionPatternCaseHandler<IEitherActionPatternMatcher<TLeft, TRight>, TLeft>
IEitherActionPatternMatcher<TLeft, TRight>.Left()
{
return new UnionPatternCaseHandler<IEitherActionPatternMatcher<TLeft, TRight>, TLeft, Unit>(
_selector.RecordAction,
this);
}

IUnionActionPatternCaseHandler<IEitherActionPatternMatcher<TLeft, TRight>, TRight>
IEitherActionPatternMatcher<TLeft, TRight>.Right()
{
return new UnionPatternCaseHandler<IEitherActionPatternMatcher<TLeft, TRight>, TRight, Unit>(
_selector.RecordAction,
this);
}

IUnionActionPatternMatcherAfterElse IEitherActionPatternMatcher<TLeft, TRight>.Else(
Action<Either<TLeft, TRight>> elseAction)
{
_selector.RecordElseAction(elseAction);
return this;
}

IUnionActionPatternMatcherAfterElse IEitherActionPatternMatcher<TLeft, TRight>.IgnoreElse()
{
_selector.RecordElseAction(Action((Either<TLeft, TRight> _) => { }));
return this;
}

void IEitherActionPatternMatcher<TLeft, TRight>.Exec() => _selector.ExecNoElse(_either);

void IUnionActionPatternMatcherAfterElse.Exec() => _selector.ExecUsingElse(_either);
}
}
17 changes: 17 additions & 0 deletions src/SuccincT/Unions/PatternMatchers/IEitherActionPatternMatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

namespace SuccincT.Unions.PatternMatchers
{
public interface IEitherActionPatternMatcher<TLeft, TRight>
{
IUnionActionPatternCaseHandler<IEitherActionPatternMatcher<TLeft, TRight>, TLeft> Left();

IUnionActionPatternCaseHandler<IEitherActionPatternMatcher<TLeft, TRight>, TRight> Right();

IUnionActionPatternMatcherAfterElse Else(Action<Either<TLeft, TRight>> elseAction);

IUnionActionPatternMatcherAfterElse IgnoreElse();

void Exec();
}
}
17 changes: 17 additions & 0 deletions src/SuccincT/Unions/PatternMatchers/IEitherFuncPatternMatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

namespace SuccincT.Unions.PatternMatchers
{
public interface IEitherFuncPatternMatcher<TLeft, TRight, TResult>
{
IUnionFuncPatternCaseHandler<IEitherFuncPatternMatcher<TLeft, TRight, TResult>, TLeft, TResult> Left();

IUnionFuncPatternCaseHandler<IEitherFuncPatternMatcher<TLeft, TRight, TResult>, TRight, TResult> Right();

IUnionFuncPatternMatcherAfterElse<TResult> Else(Func<Either<TLeft, TRight>, TResult> elseAction);

IUnionFuncPatternMatcherAfterElse<TResult> Else(TResult value);

TResult Result();
}
}
3 changes: 3 additions & 0 deletions src/SuccincT/Unions/PatternMatchers/MatchSelectorsCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ internal static MatchSelectorsForCases<T1, T2, T3, Unit, TResult> CreateSelector

internal static MatchSelectorsForCases<T1, T2, T3, T4, TResult> CreateSelectors<T1, T2, T3, T4, TResult>()
=> new MatchSelectorsForCases<T1, T2, T3, T4, TResult>();

internal static MatchSelectorsForEither<TLeft, TRight, TResult> CreateEitherSelectors<TLeft, TRight, TResult>()
=> new MatchSelectorsForEither<TLeft, TRight, TResult>();
}
}
81 changes: 81 additions & 0 deletions src/SuccincT/Unions/PatternMatchers/MatchSelectorsForEither.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using SuccincT.Functional;
using SuccincT.Options;
using SuccincT.PatternMatchers;
using System;
using System.Collections.Generic;
using static SuccincT.Utilities.NRTSupport;

namespace SuccincT.Unions.PatternMatchers
{
internal sealed class MatchSelectorsForEither<TLeft, TRight, TResult>
{
private readonly MatchFunctionSelector<TLeft, TLeft, TResult> _leftSelector;
private readonly MatchFunctionSelector<TRight, TRight, TResult> _rightSelector;
private Func<Either<TLeft, TRight>, TResult>? _elseFunction;

internal MatchSelectorsForEither()
{
_leftSelector =
new MatchFunctionSelector<TLeft, TLeft, TResult>(
x => throw new NoMatchException("No match action defined for either with Left value"));
_rightSelector =
new MatchFunctionSelector<TRight, TRight, TResult>(
x => throw new NoMatchException("No match action defined for either with Right value"));
}

internal void RecordAction<T>(Func<T, IList<T>, bool>? withTest,
Func<T, bool>? whereTest,
IList<T>? withData,
Func<T, TResult> action) =>
Selector<T>().AddTestAndAction(withTest, withData, whereTest, action);

internal void RecordAction<T>(Func<T, IList<T>, bool>? withTest,
Func<T, bool>? whereTest,
IList<T>? withData,
Func<T, Unit> action) =>
Selector<T>().AddTestAndAction(withTest, withData, whereTest, action.ToFuncOf<T, TResult>());

internal void RecordElseFunction(Func<Either<TLeft, TRight>, TResult> elseFunction) =>
_elseFunction = elseFunction;

internal void RecordElseAction(Action<Either<TLeft, TRight>> elseAction) =>
_elseFunction = elseAction.ToUnitFunc() as Func<Either<TLeft, TRight>, TResult>;

internal TResult ResultNoElse(Either<TLeft, TRight> either) =>
DetermineResultUsingDefaultIfRequired(either);

internal TResult ResultUsingElse(Either<TLeft, TRight> either)
{
var possibleResult = DetermineResult(either);
return possibleResult.HasValue ? possibleResult.Value : ElseFunction(either);
}

internal void ExecNoElse(Either<TLeft, TRight> either) =>
DetermineResultUsingDefaultIfRequired(either);

internal void ExecUsingElse(Either<TLeft, TRight> either)
{
var possibleResult = DetermineResult(either);
_ = possibleResult.HasValue ? possibleResult.Value : ElseFunction(either);
}

private MatchFunctionSelector<T, T, TResult> Selector<T>()
=> typeof(T) switch {
var t when t == typeof(TLeft) => (_leftSelector as MatchFunctionSelector<T, T, TResult>)!,
_ => (_rightSelector as MatchFunctionSelector<T, T, TResult>)!
};

private TResult DetermineResultUsingDefaultIfRequired(Either<TLeft, TRight> either)
=> either.IsLeft
? _leftSelector.DetermineResultUsingDefaultIfRequired(either.Left)
: _rightSelector.DetermineResultUsingDefaultIfRequired(either.Right);


private Option<TResult> DetermineResult(Either<TLeft, TRight> either)
=> either.IsLeft
? _leftSelector.DetermineResult(either.Left)
: _rightSelector.DetermineResult(either.Right);

private TResult ElseFunction(Either<TLeft, TRight> either) => _elseFunction!(either);
}
}
Loading

0 comments on commit 1308836

Please sign in to comment.