Update: Ian Griffiths has a more detailed blog post about this matter.
Range variables are the variables used in query expressions. For instance, consider the expression:
let length = name.Length
select new { name, length }
Here, name
and length
are range variables. In C# in Depth, I claim that range variables aren’t like any other type of variable. Eric commented that they’re not unlike iteration variables in foreach
– and since then, I’ve seen both Jamie King/Bruce Eckel’s book, and now C# 3.0 in a Nutshell (which is very good, by the way), using “iteration variable” as the name for range variables. (Update as to the reason for this: they were called iteration variables until a reasonably recent version of the spec, apparently.)
Eric made the point that there are interesting restrictions on the use of iteration variables, just like there are restrictions on the use of range variables. However, that’s not enough of a similarity from my point of view. I think of them very, very differently.
When I declare an iteration variable in a foreach
loop, the variable really “exists” in that method. It’s present in the IL for it as a local variable (or maybe a captured one). When I declare a range variable, it’s never a variable in that method – it’s just a parameter name in a lambda expression. It’s the shadow/promise/ghost of a variable – a variable yet to live, one which will only have a value when I start executing the query.
I suppose in that respect it’s closest to the parameter name given in a lambda expression or anonymous method – except that it’s valid for multiple parts of a query expression, carrying the value from place to place, via different “normal” parameters.
So, am I crazy for looking at range variables as a different type of beast, or is the rest of the world “wrong” on this one? :)
They are not exactly the same thing, but they are close enough that I don’t have a problem referring to them with a similar designation. The differences you cite are implementation details that don’t really affect how they are used.
LikeLike
Well, my clarifying question (not trying to be rhetorical here) to you would be this: what practical (intellectual) work does it do distinguish the two?
From your description of the underlying IL implementation, it sounds like there IS some important mental work that distinguishing the two would do – in which case I would agree with you. I just can’t think of a really practical example off the top of my head.
LikeLike
I don’t think of it as distinguishing the two so much, as how you go about approaching them to start with. I didn’t start off with an idea of range variables as being like iteration variables, then finding they’re different – they just look entirely different from the start, to me at least.
This isn’t entirely an implementation deal, however. It’s part of the specified query expression translation. To take a slightly simpler example, suppose we had a query expression of:
from name in names
where name.Length > 5
select name.ToUpper()
That is translated (guaranteed, not implementation-dependent) as:
names.Where(name => name.Length > 5)
.Select(name => name.ToUpper())
The “name” variable ends up being used as a parameter in two different lambda expressions. In particular, you could rewrite the above as:
names.Where(name => name.Length > 5)
.Select(other => other.ToUpper())
and the effect would be exactly the same. Other types of variable don’t let you change their names half way through their context! ;)
I suspect I’m not making myself at all clear here, but fortunately my psychic blog reading powers suggest that Ian Griffiths is likely to be making a post sometime soon with rather more depth to it… stay tuned to
http://www.interact-sw.co.uk/iangblog/
Jon
LikeLike
I agree, they’re similar but different.
LikeLike
I agree that the two concepts are similar but different. But, I would expect that for nearly all uses, the difference does not matter. So, it is not an unreasonable abstraction to think of the parameters in query comprehensions as “iteration variables”.
I don’t follow your argument about the translation to the extension-method syntax. The iteration variable is a reasonable abstraction at the query-comprehension level, but not at the method-invocation level.
Similarly, iteration variable in a for loop is a reasonable abstraction at the C# code level. But, once you you convert that code to IL, the iteration variable is not a good abstraction anymore.
I think that the problem is that you think about LINQ queries more in depth than most developers do. When you look at a LINQ query in the query comprehensions syntax, you immediately see the corresponding extension methods.
Most developers don’t. A good abstraction for most developers may not be a good abstraction for Jon Skeet, and vice versa.
LikeLike
Actually, I’m the same… I personally never compared them in my mind (of course, I’m a neophyte compared to just about everyone else you’ve mentioned or who’s posting comments here!)
I just think it’s understandable (and neither better nor worse, really) that people would find an existing idea to relate a newer concept like range variables to. If they really do share similar uses/properties/conceptual space, it will help one adopt the new concept and then one can learn how the new concept is substantially different.
LikeLike
When is a door not a door?
When it’s a jar!
When is an iteration variable most assuredly not an iteration variable?
When it’s a range variable!
A door that’s ajar is still a door, but a door that looks the same as a jar (whether ajar or not)? Now that’s just too Marilyn Manson for me!
xxx :)
LikeLike