Explain from the Questioner’s perspective, not your own

I was involved in an interesting discussion about how the C# language has evolved, and how it’s getting harder and harder to teach developers to be proficient in the language with each passing version.

I think that’s our fault.

As leaders and experienced C# developers, we too often teach and explain from our perspective: We learned C# 1.0, learned the new features in 2.0. Then, we learned LINQ and the other syntax additions in 3.0. Now, we’re looking at adding the new 4.0 features to our repertoire of techniques. It makes sense to us, because that’s how we learned.

But we are not them.

That’s an inefficient way for someone approaching C# now to learn the language. Instead, we should explain these new concepts based on how one should learn now, not how we learned. I wish I could take credit for this amazing insight, but I must credit Bjarne Stroustrup. He wrote an essay for C++ Users Journal back in 1999 asserting that the C++ community must change how they taught developers C++.  Back then, everyone teaching C++ just assumed that every novice C++ developer was a veteran C developer. And that’s how they taught. Stroustrup instead claimed that developers should learn the best C++ idioms, rather than be taught C, then how to move to C++.

Lambda Expressions as Example

Explaining Lambda expressions is a great example of what I mean in C# today.

When I explain lambda expressions to a novice C# developer, I use this example:

   1: var someNumbers = new List<int>(Enumerable.Range(-20, 200));
   2: someNumbers.RemoveAll(n => n > 30);
   3:  
   4: bool assertion = someNumbers.TrueForAll(n => n < 40);

Line 1 creates a collection with a bunch of numbers.

Line 2 introduces a lambda. It removes all numbers greater than 30 from the collection. The lambda expression “n => n > 30” describes the condition for all elements to be removed from the list. A lambda expression is a short hand description of a method: n is the parameter. The expression returns a boolean, the result of the test ‘n > 30’.

At this point, I’ll often get a few questions:

‘what type is n"?’

That’s easy: the compiler infers types for the parameters to lambda expressions. Here, the compiler infers that n must be integer, because the collection someNumbers contains integers.

‘there’s no return value’

Yes, I know that’s not a question. But it does express the confusion, and that’s how it’s often stated. I say that the compiler infers the return type. List.RemoveAll() takes a Predicate<T> as its parameter, and Predicate<T> returns a bool. therefore, the expression ‘n > 30’ must be a boolean expression. To illustrate this, I’ll modify the code to show how the compiler checks the type. The lambda expression ‘n => n.ToString()’ doesn’t compile, because n.ToString() evaluates to a string, not a boolean.

Then, I go on to the concept: a lambda expression is a mechanism that enables you to pass code (in the form of a lambda) to another method for execution later. Internally, List.RemoveAll() evaluates the lambda expression parameter for every element in the list. All elements that return true are removed.

I follow the same process to explain the TrueForAll() method call.

Yeah, but what about delegates, anonymous delegates and expression trees?

Those of you that are experienced C# developers are probably concerned: I didn’t mention that lambdas are implemented like anonymous delegates. I didn’t drill down further to state that anonymous delegates are just like delegates.

In short, I didn’t work back from a lambda expression to the equivalent C# 1.0 syntax. But, that’s the point. Unless you have been working with C# since the beginning, those concepts don’t help you. Someone starting to learn C# now doesn’t have that prior knowledge about C# 1.0, or C# 2.0.

Read my explanations above again.  They don’t dive into great detail about how a lambda expression is interpreted by the compiler. Rather, they give you a good description of how you would write code using lambda expressions, and what they do for you.

Nothing I said is wrong, but a lot of details are left out.  That’s ok, hiding details helps novices get started.  We can fill in those holes as these new C# developers develop a greater understanding, and want a deeper understanding.

At that point, they are ready for a different explanation.

Until then, we are better off explaining concepts using explanations that help them, even if those explanations are different than how we learned.

Published 16 April 2009 10:57 PM by wwagner
Ads by Lake Quincy Media

Comments

# David Morton said on 17 April, 2009 07:43 AM

Excellent post, Bill.  This should be "standard operating procedure" for anybody who enjoys helping other people learn anything, for that matter.  I think it's important for everyone to remember how to explain something to a newbie.

# Jack said on 18 April, 2009 04:00 AM

Yes, I agree with you! Delegate, then anonymous method, then Lambda expression. And then...

# Joe Gilray said on 26 April, 2009 03:09 PM

Thanks for another excellent post, Bill.  I'm a C#3 newbie starting to learn LINQ.  Your delegate() example really resonated with me!  When I see the delegate() syntax I'm confused, but I'm picking up lambda expression without knowing much (anything?) about delegates.

How do you feel about "query format" vs. lambda expressions?  When I see examples using the query format I'm turned off.  I don't like the syntax and the "verbs" are not even C# keywords.  I saw an example in a book that used "group" as a verb and a variable in the same code... ouch!

I've taken to converting the query format expressions I see in books to lambda format.  It is quite fun actually.  I'm a novice so I'm doing simple stuff, but I usually find the lambda versions simpler, shorter and more elegant.  Here is a quick example:

var u50 = from num in intList where num < 50 orderby num descending select num;

can be expressed:

var u50b = intList.Where(n => n < 50).OrderByDescending(n => n);

I also get a lot of joy out of the power of the extension methods:

u50b.ToList().ForEach(i => Console.Write("{0} ", i));

Is there a reason to teach "query format"?  Are there things that are better done in that manner?  Are there things that cannot be done with lambda expressions?

Thanks,

-Joe

# Sergio said on 26 April, 2009 11:01 PM

Hi guys, recently I started my own little blog called EasyTask.

It's basically me writting up tutorials for common programming assignments students get in college.

Please feel free to visit and tell your friends!

easytask.wordpress.com

# Nair said on 29 April, 2009 12:50 PM

Fantastic blog. In deed I read an article some time about what you explained but it went the you said at the last and it is very nonintutive than what you have mentioned here. I really enjoyed it.

Search

Go

Blog Group Links

Nascar style badges