using System; using System.Collections.Generic; using System.Linq; using System.Threading; namespace Chernobyl { /// /// Extension and utility types for and related /// types. /// public static class Func { /// /// Returns . This method makes writing in a fluent style a little easier. /// public static Func This(Func func) => func; /// /// Returns . This method makes writing in a fluent style a little easier. /// public static Func This(Func func) => func; /// /// Returns . This method makes writing in a fluent style a little easier. /// public static Func This(Func func) => func; /// /// Returns . This method makes writing in a fluent style a little easier. /// public static Func This(Func func) => func; /// /// Returns . This method makes writing in a fluent style a little easier. /// public static Func This(Func func) => func; /// /// Projects the return value of a method into a new form. /// public static Func Select(this Func source, Func selector) => () => selector(source()); /// /// Projects the return value of a method into a new form by incorporating the index of the /// invocation. /// public static Func Select(this Func source, Func selector) { return source.AppendArg().Select((s, i, _) => selector(s, i)).Bind(0); } /// /// Projects the return value of a method into a new form. /// public static Func Select(this Func source, Func selector) => source.Select((s, i, arg) => selector(s, arg)); /// /// Projects the return value of a method into a new form. /// public static Func Select(this Func source, Func selector) => source.Select((s, i, arg) => selector(s)); /// /// Projects the return value of a method into a new form by incorporating the index of the /// invocation. /// public static Func Select(this Func source, Func selector) { var index = 0; return param => { var result = selector(source(param), index, param); index++; return result; }; } /// /// Combines two methods into one method that returns the results of the two as a /// . /// public static Func> TupleJoin(Func func1, Func func2) => () => Tuple.Create(func1(), func2()); /// /// Combines two methods into one method that returns the results of the two as a /// . /// public static Func> Join(params Func[] funcs) => () => funcs.Select(f => f()); /// /// Returns a method that invokes the methods /// whenever is invoked. /// public static Func Listen(this Func function, Action listener) { return (arg1, arg2, arg3, arg4) => { var result = function(arg1, arg2, arg3, arg4); listener(result); return result; }; } /// /// Returns a method that invokes the methods /// whenever is invoked. /// public static Func Listen(this Func function, Action listener) => function.AppendArg().Listen(listener).BindArg4(0); /// /// Returns a method that invokes the methods /// whenever is invoked. /// public static Func Listen(this Func function, Action listener) => function.AppendArg().Listen(listener).BindArg3(0); /// /// Returns a method that invokes the methods /// whenever is invoked. /// public static Func Listen(this Func function, Action listener) => function.AppendArg().Listen(listener).BindArg2(0); /// /// Returns a method that invokes the methods /// whenever is invoked. /// public static Func Listen(this Func function, Action listener) => function.AppendArg().Listen(listener).Bind(0); /// /// Creates a method that, when invoked, creates a new using the /// default constructor. /// public static Func Default() where T : new() => () => new T(); /// /// Creates a method that, when invoked, creates a new using the /// default constructor. /// public static Func Default() where TConcrete : TInterface, new() => () => new TConcrete(); /// /// Creates a function that returns the value specified. /// public static Func StaticFactory(this T value) => () => value; /// /// Returns a method whose return value goes up each time it is invoked. This method is /// thread safe. /// public static Func Counter() { var index = 0; return () => Interlocked.Increment(ref index); } /// /// Returns a method that, when invoked, calls passing in the /// return value of . /// public static Func Bind(this Func method, Arg arg1) => () => method(arg1.Func()); /// /// Binds a method to the arg that projects it into a new form. /// public static Func Bind(this Func func, Func selector) => arg1 => func(selector(arg1)); /// /// Returns a method that, when invoked, calls passing in the /// return value of and . /// public static Func Bind(this Func method, Arg arg1, Arg arg2) => () => method(arg1.Func(), arg2.Func()); /// /// Returns a method that, when invoked, calls with /// as its first argument. /// public static Func BindArg1(this Func method, Arg arg1) => arg2 => method(arg1.Func(), arg2); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg2(this Func method, Arg arg2) => arg1 => method(arg1, arg2.Func()); /// /// Returns a method that, when invoked, calls passing in the /// return value of , , and . /// public static Func Bind(this Func method, Arg arg1, Arg arg2, Arg arg3) => () => method(arg1.Func(), arg2.Func(), arg3.Func()); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg1(this Func method, Arg arg1) => (arg2, arg3) => method(arg1.Func(), arg2, arg3); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg2(this Func method, Arg arg2) => (arg1, arg3) => method(arg1, arg2.Func(), arg3); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg3(this Func method, Arg arg3) => (arg1, arg2) => method(arg1, arg2, arg3.Func()); /// /// Returns a method that, when invoked, calls passing in the /// return value of , , and . /// public static Func Bind(this Func method, Arg arg1, Arg arg2, Arg arg3, Arg arg4) => () => method(arg1.Func(), arg2.Func(), arg3.Func(), arg4.Func()); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg1(this Func method, Arg arg1) => (arg2, arg3, arg4) => method(arg1.Func(), arg2, arg3, arg4); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg2(this Func method, Arg arg2) => (arg1, arg3, arg4) => method(arg1, arg2.Func(), arg3, arg4); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg3(this Func method, Arg arg3) => (arg1, arg2, arg4) => method(arg1, arg2, arg3.Func(), arg4); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg4(this Func method, Arg arg4) => (arg1, arg2, arg3) => method(arg1, arg2, arg3, arg4.Func()); /// /// Returns a method that, when invoked, calls passing in the /// return value of , , and . /// public static Func Bind(this Func method, Arg arg1, Arg arg2, Arg arg3, Arg arg4, Arg arg5) => () => method(arg1.Func(), arg2.Func(), arg3.Func(), arg4.Func(), arg5.Func()); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg1(this Func method, Arg arg1) => (arg2, arg3, arg4, arg5) => method(arg1.Func(), arg2, arg3, arg4, arg5); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg2(this Func method, Arg arg2) => (arg1, arg3, arg4, arg5) => method(arg1, arg2.Func(), arg3, arg4, arg5); /// /// Returns a method that, when invoked, calls passing in the /// return value of , , and . /// public static Func Bind( this Func method, Arg arg1, Arg arg2, Arg arg3, Arg arg4, Arg arg5, Arg arg6) => () => method(arg1.Func(), arg2.Func(), arg3.Func(), arg4.Func(), arg5.Func(), arg6.Func()); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg2(this Func method, Arg arg2) => (arg1, arg3, arg4, arg5, arg6) => method(arg1, arg2.Func(), arg3, arg4, arg5, arg6); /// /// Returns a method that, when invoked, calls with /// as its second argument. /// public static Func BindArg2(this Func method, Arg arg2) => (arg1, arg3, arg4, arg5, arg6, arg7) => method(arg1, arg2.Func(), arg3, arg4, arg5, arg6, arg7); /// /// Converts the method provided into a method which takes one, ignored, argument. /// public static Func AppendArg(this Func method) => _ => method(); /// /// Converts the method provided into a method which takes one, ignored, argument. /// public static Func AppendArg(this Func method) => (arg1, _) => method(arg1); /// /// Converts the method provided into a method which takes one, ignored, argument. /// public static Func AppendArg(this Func method) => (arg1, arg2, _) => method(arg1, arg2); /// /// Converts the method provided into a method which takes one, ignored, argument. /// public static Func AppendArg(this Func method) => (arg1, arg2, arg3, _) => method(arg1, arg2, arg3); /// /// Converts the method provided into a method which takes one, ignored, argument. /// public static Func PrependArg(this Func method) => (arg1, arg2) => method(arg2); /// /// Applies a projection to the second argument. /// public static Func SelectArg2( this Func func, Func selector) => (arg1, arg2) => func(arg1, selector(arg2)); /// /// Applies a projection to the second argument. /// public static Func SelectArg3( this Func func, Func selector) => (arg1, arg2, arg3) => func(arg1, arg2, selector(arg3)); /// /// A that returns the same value it was given. /// public static Func Passthrough() => value => value; /// /// Generates a list of items from the provided method. The returned /// will produce values infinitely. Use to restrict /// this. /// /// The method that produces a wave. /// The items generated from the . public static IEnumerable AsEnumerable(this Func method) { while (true) yield return method(); // ReSharper disable IteratorNeverReturns } // ReSharper restore IteratorNeverReturns /// /// Returns a as an . /// public static Action AsAction(this Func func) => () => func(); /// /// Returns a as an . /// public static Action AsAction(this Func func) => arg1 => func(arg1); /// /// Returns a as an . /// public static Action AsAction(this Func func) => (arg1, arg2) => func(arg1, arg2); /// /// Performs an upcast from to . /// public static Func Upcast(this Func func) where TSource : TReturn => func.Select(v => v); /// /// Returns the within the without /// invoking the until necessary. /// public static IEnumerable Switch(this Func> method) { foreach (var v in method()) yield return v; } /// /// Creates a method that replaces the value produced by with /// the value produced by with /// between each replacement. /// /// The method whose return value is to be replaced. /// The amount of space to put between each value being replaced. /// The method whose return value is to do the replacing. public static Func SkipReplace(this Func method, int distance, Func replacer) => method.AppendArg().SkipReplace(distance, _ => replacer()).Bind(0); /// /// Creates a method that replaces the value produced by with /// the value produced by with /// between each replacement. /// /// The method whose return value is to be replaced. /// The amount of space to put between each value being replaced. /// The method whose return value is to do the replacing. public static Func SkipReplace(this Func method, int distance, Func replacer) { distance.Throw(nameof(distance)).IfLessThan(0); // We add one to index so that zero doesn't get included. return method.Replace((param, index) => (index + 1) % distance == 0, replacer); } /// /// Creates a method that replaces the value produced by with /// the value produced by when dictated by . /// /// The method whose return value is to be replaced. This method is /// only invoked when returns false. /// The method that determines when a value is to be replaced. The /// first parameter is the current invocation index starting from zero. /// The method whose return value is to do the replacing. This method /// is only invoked when returns true. public static Func Replace(this Func method, Func predicate, Func replacer) => method.AppendArg().Replace((_, index) => predicate(index), _ => replacer()).Bind(0); /// /// Creates a method that replaces the value produced by with /// the value produced by when dictated by . /// /// The method whose return value is to be replaced. This method is /// only invoked when returns false. /// The method that determines when a value is to be replaced. The /// first parameter is the parameter passed to the returned method. /// The method whose return value is to do the replacing. This method /// is only invoked when returns true. public static Func Replace(this Func method, Func predicate, Func replacer) => method.Replace((param, _) => predicate(param), replacer); /// /// Creates a method that replaces the value produced by with /// the value produced by when dictated by . /// /// The method whose return value is to be replaced. This method is /// only invoked when returns false. /// The method that determines when a value is to be replaced. The /// first parameter is the parameter passed to the returned method. The second parameter is /// the current invocation index starting from zero. /// The method whose return value is to do the replacing. This method /// is only invoked when returns true. public static Func Replace(this Func method, Func predicate, Func replacer) { var index = 0; return param => { var result = predicate(param, index) ? replacer(param) : method(param); index++; return result; }; } /// Returns a method that negates the returned value of the method provided. public static Func Not(this Func func) => () => !func(); /// Returns a method that negates the returned value of the method provided. public static Func Not(this Func func) => arg1 => !func(arg1); /// /// Returns a method that returns true if the value passed to it is null, false if otherwise. /// public static Func NotNull() => arg1 => arg1 != null; /// /// Returns a predicate that returns true if the value provided to it is different from the /// the value provided on the last invocation. Used with methods like /// /// /// The instance to use for comparison. Uses /// if nothing provided. public static Func Changed(IEqualityComparer comparer = null) where T : IEquatable { comparer = comparer ?? EqualityComparer.Default; T previous = default; return current => { if (comparer.Equals(previous, current) == false) { previous = current; return true; } return false; }; } } }