Skip to content

Instantly share code, notes, and snippets.

@mshwf
Created June 20, 2018 20:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mshwf/79294e2f35ec1005f43ef5557d95b9af to your computer and use it in GitHub Desktop.
Save mshwf/79294e2f35ec1005f43ef5557d95b9af to your computer and use it in GitHub Desktop.
Full optional type implementation
//credits to Zoran Horvat, author of Making Your C# Code More Object-oriented course.
namespace MakingYourCSharpCodeMoreObjectOriented
{
public class Option<T> : IOption<T>
{
private IEnumerable<T> Content { get; }
private Option(IEnumerable<T> content)
{
Content = content;
}
public static Option<T> Some(T value) =>
new Option<T>(new[] { value });
public static Option<T> None() =>
new Option<T>(new T[0]);
public IFiltered<T> When(Func<T, bool> predicate)
{
return
Content
.Select(item => WhenSome(item, predicate))
.DefaultIfEmpty(new NoneNotMatchedAsSome<T>())
.Single();
}
private IFiltered<T> WhenSome(T value, Func<T, bool> predicate) =>
predicate(value) ? new SomeMatched<T>(value) : (IFiltered<T>)new SomeNotMatched<T>(value);
public IFiltered<T> WhenSome()
{
return
Content
.Select<T, IFiltered<T>>(item => new SomeMatched<T>(item))
.DefaultIfEmpty(new NoneNotMatchedAsSome<T>())
.Single();
}
public IFilteredNone<T> WhenNone()
{
return
Content
.Select<T, IFilteredNone<T>>(item => new SomeNotMatchedAsNone<T>(item))
.DefaultIfEmpty(new NoneMatched<T>())
.Single();
}
}
internal class NoneMatched<T> : IFilteredNone<T>, IFilteredNoneActionable<T>
{
public IActionable<T> Do(Action action) =>
new ActionResolved<T>(action);
public IMapped<T, TResult> MapTo<TResult>(Func<TResult> mapping) =>
new MappingResolved<T, TResult>(mapping());
}
internal class SomeNotMatchedAsNone<T> : IFilteredNone<T>
{
private T Value { get; }
public SomeNotMatchedAsNone(T value)
{
Value = value;
}
public IActionable<T> Do(Action action) =>
new ActionOnSomeNotResolved<T>(Value);
public IMapped<T, TResult> MapTo<TResult>(Func<TResult> mapping) =>
new MappingOnSomeNotResolved<T, TResult>(Value);
}
internal class NoneNotMatchedAsSome<T> : IFiltered<T>
{
public IActionable<T> Do(Action<T> action) =>
new ActionOnNoneNotResolved<T>();
public IMapped<T, TResult> MapTo<TResult>(Func<T, TResult> mapping) =>
new MappingOnNoneNotResolved<T, TResult>();
}
internal class ActionOnNoneNotResolved<T> : IActionable<T>
{
public IFilteredActionable<T> When(Func<T, bool> predicate) =>
new NoneNotMatchedAsSome<T>();
public IFilteredActionable<T> WhenSome() =>
new NoneNotMatchedAsSome<T>();
public IFilteredNoneActionable<T> WhenNone() =>
new NoneMatched<T>();
public void Execute() { }
}
internal class MappingOnNoneNotResolved<T, TResult> : IMapped<T, TResult>
{
public IFilteredMapped<T, TResult> When(Func<T, bool> predicate) =>
new NoneNotMatchedForMapping<T, TResult>();
public IFilteredMapped<T, TResult> WhenSome() =>
new NoneNotMatchedForMapping<T, TResult>();
public IFilteredNoneMapped<T, TResult> WhenNone() =>
new NoneMatchedForMapping<T, TResult>();
public TResult Map()
{
throw new InvalidOperationException();
}
}
internal class NoneMatchedForMapping<T, TResult> : IFilteredNoneMapped<T, TResult>
{
public IMapped<T, TResult> MapTo(Func<TResult> mapping) =>
new MappingResolved<T, TResult>(mapping());
}
internal class NoneNotMatchedForMapping<T, TResult> : IFilteredNoneMapped<T, TResult>, IFilteredMapped<T, TResult>
{
public IMapped<T, TResult> MapTo(Func<TResult> mapping) =>
new MappingOnNoneNotResolved<T, TResult>();
public IMapped<T, TResult> MapTo(Func<T, TResult> mapping) =>
new MappingOnNoneNotResolved<T, TResult>();
}
internal class SomeNotMatched<T> : IFiltered<T>, IFilteredNoneActionable<T>
{
private T Value;
public SomeNotMatched(T value)
{
Value = value;
}
public IActionable<T> Do(Action<T> action)
{
return new ActionOnSomeNotResolved<T>(Value);
}
public IActionable<T> Do(Action action) =>
new ActionOnSomeNotResolved<T>(Value);
public IMapped<T, TResult> MapTo<TResult>(Func<T, TResult> mapping)
{
return new MappingOnSomeNotResolved<T, TResult>(Value);
}
}
internal class MappingOnSomeNotResolved<T, TResult> : IMapped<T, TResult>
{
private T Value { get; }
public MappingOnSomeNotResolved(T value)
{
Value = value;
}
public IFilteredMapped<T, TResult> When(Func<T, bool> predicate) =>
predicate(Value)
? (IFilteredMapped<T, TResult>)new SomeMatchedForMapping<T, TResult>(Value)
: new SomeNotMatchedForMapping<T, TResult>(Value);
public IFilteredMapped<T, TResult> WhenSome() =>
new SomeMatchedForMapping<T, TResult>(Value);
public IFilteredNoneMapped<T, TResult> WhenNone() =>
new SomeNotMatchedAsNoneForMapping<T, TResult>(Value);
public TResult Map()
{
throw new InvalidOperationException();
}
}
internal class SomeNotMatchedAsNoneForMapping<T, TResult> : IFilteredNoneMapped<T, TResult>
{
private T Value { get; }
public SomeNotMatchedAsNoneForMapping(T value)
{
Value = value;
}
public IMapped<T, TResult> MapTo(Func<TResult> mapping) =>
new MappingOnSomeNotResolved<T, TResult>(Value);
}
internal class SomeMatchedForMapping<T, TResult> : IFilteredMapped<T, TResult>
{
private T Value;
public SomeMatchedForMapping(T value)
{
Value = value;
}
public IMapped<T, TResult> MapTo(Func<T, TResult> mapping) =>
new MappingResolved<T, TResult>(mapping(Value));
}
internal class SomeNotMatchedForMapping<T, TResult> : IFilteredMapped<T, TResult>
{
private T Value;
public SomeNotMatchedForMapping(T value)
{
Value = value;
}
public IMapped<T, TResult> MapTo(Func<T, TResult> mapping) =>
new MappingOnSomeNotResolved<T, TResult>(Value);
}
internal class ActionOnSomeNotResolved<T> : IActionable<T>
{
private T Value { get; }
public ActionOnSomeNotResolved(T value)
{
Value = value;
}
public IFilteredActionable<T> When(Func<T, bool> predicate) =>
predicate(Value) ? (IFilteredActionable<T>)new SomeMatched<T>(Value) : new SomeNotMatched<T>(Value);
public IFilteredActionable<T> WhenSome() =>
new SomeMatched<T>(Value);
public IFilteredNoneActionable<T> WhenNone() =>
new SomeNotMatched<T>(Value);
public void Execute() { }
}
internal class SomeMatched<T> : IFiltered<T>
{
private T Value;
public SomeMatched(T value)
{
Value = value;
}
public IActionable<T> Do(Action<T> action)
{
return new ActionResolved<T>(() => action(Value));
}
public IMapped<T, TResult> MapTo<TResult>(Func<T, TResult> mapping)
{
return new MappingResolved<T, TResult>(mapping(Value));
}
}
internal class MappingResolved<T, TResult> : IMapped<T, TResult>, IFilteredMapped<T, TResult>, IFilteredNoneMapped<T, TResult>
{
private TResult Result { get; }
public MappingResolved(TResult result)
{
Result = result;
}
public IFilteredMapped<T, TResult> When(Func<T, bool> predicate) => this;
public IFilteredMapped<T, TResult> WhenSome() => this;
public IFilteredNoneMapped<T, TResult> WhenNone() => this;
public IMapped<T, TResult> MapTo(Func<T, TResult> mapping) => this;
public IMapped<T, TResult> MapTo(Func<TResult> mapping) => this;
public TResult Map() => Result;
}
internal class ActionResolved<T> : IActionable<T>, IFilteredActionable<T>, IFilteredNoneActionable<T>
{
private Action Action { get; }
public ActionResolved(Action action)
{
Action = action;
}
public IFilteredActionable<T> When(Func<T, bool> predicate) => this;
public IFilteredActionable<T> WhenSome() => this;
public IFilteredNoneActionable<T> WhenNone() => this;
public IActionable<T> Do(Action<T> action) => this;
public IActionable<T> Do(Action action) => this;
public void Execute() => Action();
}
public interface IFilteredNoneActionable<T>
{
IActionable<T> Do(Action action);
}
public interface IOption<T>
{
IFiltered<T> When(Func<T, bool> predicate);
IFiltered<T> WhenSome();
IFilteredNone<T> WhenNone();
}
public interface IFilteredNone<T>
{
IActionable<T> Do(Action action);
IMapped<T, TResult> MapTo<TResult>(Func<TResult> mapping);
}
public interface IFiltered<T> : IFilteredActionable<T>
{
IMapped<T, TResult> MapTo<TResult>(Func<T, TResult> mapping);
}
public interface IMapped<T, TResult>
{
IFilteredMapped<T, TResult> When(Func<T, bool> predicate);
IFilteredMapped<T, TResult> WhenSome();
IFilteredNoneMapped<T, TResult> WhenNone();
TResult Map();
}
public interface IFilteredNoneMapped<T, TResult>
{
IMapped<T, TResult> MapTo(Func<TResult> mapping);
}
public interface IFilteredMapped<T, TResult>
{
IMapped<T, TResult> MapTo(Func<T, TResult> mapping);
}
public interface IFilteredActionable<T>
{
IActionable<T> Do(Action<T> action);
}
public interface IActionable<T>
{
IFilteredActionable<T> When(Func<T, bool> predicate);
IFilteredActionable<T> WhenSome();
IFilteredNoneActionable<T> WhenNone();
void Execute();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment