using System; namespace Sprache { /// /// Represents an optional result. /// /// The result type. public interface IOption { /// /// Gets a value indicating whether this instance is empty. /// bool IsEmpty { get; } /// /// Gets a value indicating whether this instance is defined. /// bool IsDefined { get; } /// /// Gets the matched result or a default value. /// /// T GetOrDefault(); /// /// Gets the matched result. /// T Get(); } /// /// Extensions for . /// public static class OptionExtensions { /// /// Gets the value or else returns a default value. /// /// The result type. /// /// The default value. /// public static T GetOrElse(this IOption option, T defaultValue) { if (option == null) throw new ArgumentNullException(nameof(option)); return option.IsEmpty ? defaultValue : option.Get(); } /// /// Maps a function over the value or else returns an empty option. /// /// The input type. /// The output type. /// The option containing the value to apply to. /// The function to apply to the value of . /// An options result containing the result if there was an input value. public static IOption Select(this IOption option, Func map) { if (option == null) throw new ArgumentNullException(nameof(option)); return option.IsDefined ? (IOption) new Some(map(option.Get())) : new None(); } /// /// Binds the value to a function with optional result and flattens the result to a single optional. /// A result projection is applied aftherwards. /// /// The input type. /// The output type of . /// The final output type. /// The option containing the value to bind to. /// The function that receives the input values and returns an optional value. /// The function that is projects the result of . /// An option result containing the result if there were was an input value and bind result. public static IOption SelectMany(this IOption option, Func> bind, Func project) { if (option == null) throw new ArgumentNullException(nameof(option)); if (option.IsEmpty) return new None(); var t = option.Get(); return bind(t).Select(u => project(t,u)); } /// /// Binds the value to a function with optional result and flattens the result to a single optional. /// /// The input type. /// The output type. /// The option containing the value to bind to. /// The function that receives the input values and returns an optional value. /// An option result containing the result if there were was an input value and bind result. public static IOption SelectMany(this IOption option, Func> bind) => option.SelectMany(bind, (_,x) => x); } internal abstract class AbstractOption : IOption { public abstract bool IsEmpty { get; } public bool IsDefined { get { return !IsEmpty; } } public T GetOrDefault() { return IsEmpty ? default(T) : Get(); } public abstract T Get(); } internal sealed class Some : AbstractOption { private readonly T _value; public Some(T value) { _value = value; } public override bool IsEmpty { get { return false; } } public override T Get() { return _value; } } internal sealed class None : AbstractOption { public override bool IsEmpty { get { return true; } } public override T Get() { throw new InvalidOperationException("Cannot get value from None."); } } }