using System.Collections.Generic;
public static void Main()
var people = new Person[0];
var filtered = people.Apply(BooleanConnective.AndAlso, p => p.FirstName.StartsWith("J"), p => p.FirstName.StartsWith("A"));
public string FirstName { get; }
public string LastName { get; }
public interface IBooleanConnective
bool Operator(bool left, bool right);
BooleanGreed Greed { get; }
public sealed class BooleanConnective : IBooleanConnective
private BooleanConnective(
Func<bool, bool, bool> @operator,
this.OperatorFunction = @operator;
public static IBooleanConnective AndAlso { get; } = new BooleanConnective((left, right) => left && right, BooleanGreed.LazyFalse);
public static IBooleanConnective OrElse { get; } = new BooleanConnective((left, right) => left || right, BooleanGreed.LazyTrue);
public BooleanGreed Greed { get; }
private Func<bool, bool, bool> OperatorFunction { get; }
public bool Operator(bool left, bool right)
return this.OperatorFunction(left, right);
public static IEnumerable<T> Apply<T>(
this IEnumerable<T> source,
IBooleanConnective connective,
params Predicate<T>[] predicates)
return source.Where(t => MergePredicates(t, predicates, connective));
private static bool MergePredicates<T>(T t, IEnumerable<Predicate<T>> predicates, IBooleanConnective connective)
var e = predicates.GetEnumerator();
var value = e.Current(t);
switch (connective.Greed)
case BooleanGreed.Greedy:
case BooleanGreed.LazyFalse:
value = connective.Operator(value, e.Current(t));
switch (connective.Greed)
case BooleanGreed.Greedy:
case BooleanGreed.LazyFalse: