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;
};
}
}
}