Workaround for: Cannot assign lambda expression to a range variable

I stumbled recently on a LINQ limitation. This piece of code cannot compile (even R# doesn’t detect any error) :

using System;
using System.Linq;

class Program {
   static void Main(string[] args) {
      var query = from i in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
                  let fct = (int x) => (x - 5) * (x - 5)
                  where fct(i) > fct(i - 1)
                  select i;
      foreach (var i in query) { Console.WriteLine(i); }
   }
}

The C# compiler error, provoked by fct, is actually Cannot assign lambda expression to a range variable. Trying to replace the lambda by an anonymous method didn’t help, the same error kept popping.

After mulling over a bit I found the following workaround:

using System;
using System.Linq;

class Program {
   static void Main(string[] args) {
      var query = from i in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
                  let fct = Function((int x) => (x - 5) * (x - 5))
                  where fct(i) > fct(i - 1)
                  select i;
      foreach (var i in query) { Console.WriteLine(i); }
   }

   static Func<T, TResult> Function<T, TResult>(Func<T, TResult> f) {
      return f;
   }
}

I am not sure it is the best workaround possible, dear reader, can you imagine a cleaner one?

Revised:

  • From the answer, 2 nice possibilities were pinpointed: casting to Func<,> or creating explicitly a Func<,>. This is indeed a nicer approach in the sense that it doesn’t involve an extra static method and it is a pure language construct. A minor downside compared to the Function solution proposed is that it forces to precise explicitly the TResult type.
let fct = new Func<int,int>((x => (x - 5) * (x - 5)))
let fct = (Func<int,int>) ((x => (x - 5) * (x - 5)))

 

  • I haven’t been explicit enough in the post, the tricky thing is to create a function inside the LINQ query definition, defining the function before the query is not an option.
  • Repeating the function definition is not an option, for example purpose here the function defined in the LINQ query is trivial, what I am looking for is a solution for the general case where the function can be complex.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Jon Skeet

     Here’s an alternative:

    public static class Extensions {        public static Func Lambda(this T ignored,                                                      Func func) {        return func;    }}

    then

          var query = from i in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }                  let fct = i.Lambda(x => (x – 5) * (x + 5))                  where fct(i) > fct(i – 1)                  select i;

    Yes, you need the horrible extension method – but then everything’s inferred.

  • http://profiles.google.com/mike.strobel Mike Strobel

     This is not actually a limitation of range variables; you could not do this, either:

    var fct = (int x) => (x - 5) * (x - 5);The C# compiler cannot create a delegate from a lambda expression (or any anonymous method) without a type hint.  This is a consequence of how delegates tie into the type system.   Unlike some functional languages, the C# compiler will never generate a delegate type for an untyped lambda expression, and it can only bind a lambda to a delegate if it knows the desired type (either from the assignment target, or in the case of method overload resolution, by finding the best match).  In the case of assignments like above (or your range variable), you must tell the compiler which delegate type it should attempt to bind to.  As others have suggested, this can be done either by a cast expression or via a delegate constructor.  Your original solution also works, as the compiler can infer the delegate type from the your Function method’s signature.

  • Erik_F

    Do you know why this limitation exists? I suspect it’s a combination of at least two factors:

    1) the compiler cannot infer the type of a lambda in most cases — is it a Func or an Expression? ‘var blah = () => {};’ doesn’t work for this reason, so I don’t see how ‘let blah = () => {}’ would work either.

    2) Delegates can’t be readily used by most non-default LINQ providers.

    Have you tried this:

    var query = from i in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }                      
                          let fct = new Func(x => (x-5) * (x+5))                      where fct(i) > fct(i – 1)                      select i;
    Where you’re passing a lambda to a constructor and assigning the resulting object to the range variable. I’m not on my dev machine right now so I can’t test that myself, but for some reason I don’t think that works either.

  • http://blog.madd0.com madd0

    @twitter-21576088:disqus ‘s code should work. An alternative syntax:
    var query = from i in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
                let fct = new Func((int x) => (x - 5) * (x - 5))
                where fct(i) > fct(i - 1)
                select i;

    Fun fact: I wanted to see what the diffrence was “behind the scenes” between Mark’s syntax and mine using Reflector (spoiler: none apparently), and guess what the generated C# looks like:

    IEnumerable query = from i in new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }                         let fct = x => (x – 5) * (x – 5)                         where fct(i) > fct(i – 1)                         select i;

  • Anonymous

     @twitter-21576088:disqus Snippet      var query = from i in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }                  let fct = (Func)  (int x) => (x - 5) * (x - 5)                  where fct(i) > fct(i - 1)                  select i;
    have you compile your code, it doesnt seems to work for me

    @twitter-30174748:disqus : obvioulsy I want a general purpose solution and no for a simple fct

    @google-a390490e76cf31488700ebcd43b88ce2:disqus : Sure a variable function do the trick, but I want to define the function in the query itself

  • Anonymous

     @twitter-21576088:disqus Snippet      var query = from i in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }                  let fct = (Func)  (int x) => (x - 5) * (x - 5)                  where fct(i) > fct(i - 1)                  select i;
    have you compile your code, it doesnt seems to work for me

    @twitter-30174748:disqus : obvioulsy I want a general purpose solution and no for a simple fct

    @google-a390490e76cf31488700ebcd43b88ce2:disqus : Sure a variable function do the trick, but I want to define the function in the query itself

  • http://twitter.com/markrendle Mark Rendle

     Casting the lambda to Func is as expressive, and doesn’t require the addition of a Function method elsewhere in code.

    var query = from i in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
                            let fct = (Func)(x => (x - 5) * (x - 5))
                            where fct(i) > fct(i - 1)
                            select i;

  • http://twitter.com/stikves sukru tikves

    If the function is as simple as the in the example, why don’t you inline it  in the ”
    fct(i) > fct(i - 1)” comparison?

  • Bas Geertsema

    You could just define a variable for the function. Like this:

              Func fct = x => (x – 5)*(x – 5);

              var query = from i in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }                     

                          where fct(i) > fct(i – 1)

                          select i;

              foreach (var i in query) { Console.WriteLine(i); }    

  • Bas Geertsema

    You could just use a variable before the query.