Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linq point free syntax for multiple predicates #2737

Open
stuartstein777 opened this issue Aug 23, 2019 · 7 comments
Open

Linq point free syntax for multiple predicates #2737

stuartstein777 opened this issue Aug 23, 2019 · 7 comments

Comments

@stuartstein777
Copy link

@stuartstein777 stuartstein777 commented Aug 23, 2019

Given the following LINQ:

void Main() => Enumerable.Range(1, 20).Where(f => Foo(f) && Bar(f)).PrintAll();

public bool Foo(int x) => x % 2 == 0;
public bool Bar(int y) => y % 3 == 0;

If I only had one function in the Where I could write

Enumerable.Range(1, 20).Where(Foo)

Is their any reason why if I have multiple functions and they all take just one parameter, and it's the type of the item in the collection, I could not have to write f=> Foo(f) && Bar(f)

e.g.

Enumerable.Range(1, 20).Where(Foo && Bar).PrintAll();

?

If this makes no sense as a language suggestion or is impossible, feel free to close.

Thanks :)

@dogbiscuituk

This comment has been minimized.

Copy link

@dogbiscuituk dogbiscuituk commented Aug 23, 2019

I'd have loved to read Eric Lippert's take on this, while he was on the C# language design team. I suspect it would have been something like "A single suitable method group can be inferred to a predicate, but this would require that any general expression involving compatible method groups also be so inferable (?) - such would involve creating a new internal algebra, where the primitives are method groups, and the operations are any combination of operators, not just boolean ones. For example, why not "Foo > Bar", where Foo(f) and Bar(f) return numeric values? That's a lot of work!"

@Tom01098

This comment has been minimized.

Copy link

@Tom01098 Tom01098 commented Aug 23, 2019

I think it could be confusing - looking at your proposed syntax, Foo or Bar could be booleans and not delegates.

@Gamic

This comment has been minimized.

Copy link

@Gamic Gamic commented Aug 31, 2019

You could solve this by declaring a function like
public bool FooAndBar (int x) => Foo(x) && Bar(x);
or having some function like:
public Func<T, bool> And<T> (Func<T, bool> f, Func<T, bool> g) => x => f(x) && g(x);
public Func<int, bool> FooAndBar () => And(Foo, Bar);
Both of which can then be used as
Enumerable.Range(1, 20).Where(FooAndBar).PrintAll();
My sample on this is something like:

namespace Examples {
	static class Extensions{
		public static IEnumerable<T> PrintAll<T>(this IEnumerable<T> ts) {
			foreach (var t in ts) {
				Console.WriteLine(t);
				yield return t;
			}
		}
	}
	class Program {
		public static bool Foo(int x) => x % 2 == 0;
		public static bool Bar(int y) => y % 3 == 0;
		public static Func<T, bool> And<T>(Func<T, bool> f, Func<T, bool> g) => x => f(x) && g(x);
		public static Func<int, bool> FooAndBar => And<int>(Foo, Bar);
		static void Main()
			=>	Enumerable.Range(1, 20)
					   .Where(FooAndBar)
					   .PrintAll()
					   .ToList();

	}
}
@ronnygunawan

This comment has been minimized.

Copy link

@ronnygunawan ronnygunawan commented Sep 2, 2019

Here, I wrote you an extension method:

public static class LinqExtensions {
    public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source,
        params Func<TSource, bool>[] predicates)
    {
        IEnumerable<TSource> result = source;
        foreach (var predicate in predicates)
        {
            result = result.Where(predicate);
        }
        return result;
    }
}

Usage:

Enumerable.Range(1, 20).Where(Foo, Bar).PrintAll();
@dogbiscuituk

This comment has been minimized.

Copy link

@dogbiscuituk dogbiscuituk commented Sep 2, 2019

I think the original suggestion was for something more general, that could handle cases like

Where(Foo && (Bar || Baz))

etc., with Foo, Bar, Baz all being method groups.

@ronnygunawan

This comment has been minimized.

Copy link

@ronnygunawan ronnygunawan commented Sep 3, 2019

I think I see the point now. It's like function composition in F#. Perhaps we can have something like this:

Func<int, bool> Foo = x => x % 2 == 0;
Func<int, bool> Bar = x => x % 3 == 0;
Func<int, int> Square = x => x * x;

// Pipelining
var F1 = Square |> Foo;
// F1 should be equivalent to:
Func<int, bool> F1 = x => Foo.Invoke(Square.Invoke(x));
// or even better with Expression rewrite:
Func<int, bool> F1 = x => (x * x) % 2 == 0;

// Binary
var F2 = Foo && Bar;
// F2 should be equivalent to:
Func<int, bool> F2 = x => Foo.Invoke(x) && Bar.Invoke(x);
// or with Expression rewrite:
Func<int, bool> F2 = x => (x % 2 == 0) && (x % 3 == 0);

// Unary
var F3 = !Bar;
// F3 should be equivalent to:
Func<int, bool> F3 = x => !Bar.Invoke(x);
// or with Expression rewrite:
Func<int, bool> F3 = x => !(x % 3 == 0);

// Then we can write this:
Enumerable.Range(1, 20).Where(Foo && Bar).PrintAll();
Enumerable.Range(1, 20).Where((Square |> Foo) && !Bar).PrintAll();
@smartcodinghub

This comment has been minimized.

Copy link

@smartcodinghub smartcodinghub commented Sep 10, 2019

This old post could help you: https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/

With this, you can have something like Enumerable.Range(1, 20).Where(Foo.And(Bar)).PrintAll();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants
You can’t perform that action at this time.