Take the 2-minute tour ×

We know about the deferred execution or lazy evaluation features introduced in C#. But at times, people become confused with them. Because there is no significant difference. You can only know if you know the internals. Shouldn't there be a syntactical difference between them to remove confusion?

I think the keyword let of F# can be imported to C# to specify the lazy evaluations. Like-

let selectedItems = Items.Where(i => i.Count < 5);

What do you guys think?

In reply to the first answer, I am not proposing that the let keyword is to be used in the places where the lazy evaluation is already happening according to the present compiling model. Then it will be of no use actually. I am proposing that the let keyword should define the type of execution. To be clear-

var selectedItems = Items.Where(i => i.Count < 5).ToList();

will be executed eagerly by present model. But my proposal is, if we use

let selectedItems = Items.Where(i => i.Count < 5).ToList();

it will be executed later, just when required. Though this is not desirable as it will return different lists of same items each time. The 'thing' defined with let keyword can be thought of a constant 'expression' like F#.

Update 2:
Linking this SO answer as a proof of people's annoyance with deferred execution-


share|improve this question
I don't know any C#, but isn't that line lazy simply because Where is an enumerator, not because C# has real lazy evaluation at language level (save, of course, for enumerators with yield *)? And .ToList() forces complete evaluation anyway? You may be the one who's confused with it ;) –  delnan May 7 '11 at 10:21
I suspect this question might get more answers on SO? –  Steve Haigh May 7 '11 at 10:24
@delnan Yes you are right as IEnumerables are lazy and .ToList() makes it eager. It's trivial to use .ToList() to force eager evaluation. But to remove all confusions, you have to know the full list of the extension methods those force eager evaluation. And anyone can define a new extension method. The problem is, you have to check and know to get the right behavior. You cannot just glance and get it. –  Gulshan May 7 '11 at 11:36
@delnan And after using .Tolist, we can proceed further using IEnumerable extension methods to it like ...ToList().Select(...);. Because List in C# implements IEnumerable. Ans it means parts of same statement is executed differently. The part upto .ToList() is evaluated eagerly but later part is evaluated lazily. Too confusing I think. –  Gulshan May 7 '11 at 12:28
@Gulshan: From my experience in Python (which has similar things, basically just different names) I can't understand why this should be confusing. Yeah, so you filter an eagerly-constructed sequence lazily, lazily map it, and then perhaps force the result to be evaluated eagerly. What's so hard about that and why does it matter (semantically)? I'd understand the complaint if the same expression would be evaluated differently depending on context, but that's not the case. .Where is always lazy, .ToList is always eager, etc. –  delnan May 7 '11 at 12:57
show 2 more comments

4 Answers

up vote 12 down vote accepted

First off, it is very hard to tell what is "deferred execution":

User user = GetCurrentUser();
bool hasPermission = user.HasPermissionToAccessPrinter();

Is that "deferred execution"? The question of whether the user has the ability, right now, to access the printer can change over time. When you create the "user" object, it is very unlikely that doing so caches in the user object whether the user has the right to access the printer. The work to obtain the answer to that question is probably deferred until the question is asked.

How is that any different from

var query = customers.Where(c=>c.City=="London");
var first = query.FirstOrDefault();


Same thing. The "where" is run eagerly and gives back an object that knows how to answer questions, like "what is the first customer that satisfies the query?" The answer might change over time so you don't want to cache the result when the query object is created. You want to calculate the result when the question is asked, not before.

How can you tell the difference between these two cases? I think you would probably not characterize the first one as "deferred execution" but would characterize the second one as "deferred execution". I'm not sure that the idea of "deferred execution" even makes sense. All execution is deferred until a method is called.

Second, I think the compiler already supports the feature of "deferred execution" that you want. When you say:

int x;
let x = Foo();  // Does not call Foo() right now
M(x); // this calls Foo() and passes the result to x

that's just the same as:

Func<int> x;
x = ()=>Foo();

So, we already have the feature you want. A Func<T> is how you represent a deferred T that will be computed synchronously on demand. Similarly, a Task<T> is how you represent an asynchronously deferred T that will trigger a callback when it is calculated. And a Lazy<T> is a cached deferred T that is calculated synchronously. All these features are already in the type system.

We could lift them into the language proper if we wanted to. We are doing so with Task<T> in the next version. But the benefit of doing so has to be really high; with the async feature, it makes the code much simpler. The "let" feature you propose is not much simpler than just making a Func<T> and invoking it.

share|improve this answer
Would you please explain- "All execution is deferred until a method is called." Thanks in advance. –  Gulshan Nov 4 '11 at 6:38
@Gulshan: Pick a method, any method. The method does work; either it produces a side effect, or it computes a result, or both. Is the side effect produced or the result computed before the method is called? Usually not. That work is deferred until the method is called, at which point the work happens. Methods are a mechanism for deferring work until you need the result of that work. –  Eric Lippert Nov 4 '11 at 13:22
@EricLippert I find having to manually type Func<T1, T2> or Action<T> a little uncool at times, because var cannot infer if it is an Action or Func. Maybe the let idea could be used in another sense: var automatically infers a lambda to Action and let infers to Func. Wouldn't that be nice? –  André Leria Feb 14 at 12:53
add comment

Just let wouldn't cut it, you would need to have a lazy attribute or keyword that got applied to every method, property and maybe class level field that was lazy so that the compiler would know that you should use let rather than var and then it would probably need to just take your word for it, or employ some pretty complex logic to work out if the evaluation really was lazy. And then for all that extra overhead and confusion for programmers of yet another keyword or attribute and concept I'm not really sure what you would get. So I don't think that this is a good idea.

share|improve this answer
add comment

If your intent is to clearly show that the evaluation of a value must be postponed until used, you can simply use Lazy in .NET Framework 4.

It'll do the same thing as the let in your example, but does not require neither to add a new keyword to the language, nor to change the language behavior.

share|improve this answer
The lazy can be used to instantiate objects lazily but it cannot make expressions which will be evaluated lazily. Or can it? It's true that one of the caveats of my idea is- it will alter the present language behavior. But still I think it will be helpful. –  Gulshan May 7 '11 at 11:18
add comment

Firstly: I don't like the term "lazy". It's too ambiguous, IMO.

Secondly, you claim that these features were introduced into C# - where, exactly? Iterator blocks are lazy in that the code you write in them doesn't get executed until you start iterating over the sequence - but anything you can write with an iterator block can be written without one. Likewise lambda expressions give the ability to express code to be executed later - but again, they don't do anything that can't be done without them.

Thirdly, you claim "you can only know if you know the internals" - and cite Where as an example. The documentation for Enumerable.Where is pretty clear on this:


That's not knowing the internals - that's knowing the documented functionality. If you don't know what a method does, you have no business calling it.

So a) I don't really see a problem, and b) I don't really like your proposed solution. It wouldn't cover all cases (code could still easily be written to give lazy semantics even when used without let) which would cause more confusion IMO.

If I want something to be evaluated lazily, I can do that already using Lazy<T> pretty easily, and explicitly.

share|improve this answer
I think I could not expressed myself properly. 'lazy' and 'deferred execution' are different things and I mixed them up. I think my proposal is- (1)Every statement in C# will be executed eagerly by default (whether it contains something lazy or not). (2)let will be used to define expressions which will always be executed each time it is called (deferred execution). When I said internals I actually meant the documentation. And the problem is, we have to check the documentation, cannot get from the syntax. And please see my other comments in this page. Hope that will make my idea clearer. –  Gulshan May 8 '11 at 16:01
Once you asked a question and marked this as the answer- stackoverflow.com/questions/215548/… –  Gulshan May 8 '11 at 16:21
@Gulshan: Yes, delayed execution is hard to get your head round - but I don't think giving it different syntax would help. As I said in my answer, if you don't read the documentation for something, you shouldn't be surprised when it doesn't behave as you expect. Documentation is different to "internals" (i.e. stuff you shouldn't have to know about). –  Jon Skeet May 8 '11 at 17:19
I admit I made a mistake writing 'internals' it should have been 'documentation'. I think it's better to have things working as I expect without checking documentation. But surely you are right- "if you don't read the documentation for something, you shouldn't be surprised when it doesn't behave as you expect." –  Gulshan May 9 '11 at 3:13
@Gulshan: It's always nice if that can happen, but I don't expect it - and I don't think that's a good enough reason to introduce a language change which would only sometimes help. –  Jon Skeet May 9 '11 at 5:21
show 1 more comment

Your Answer


