Fabulous Adventures In Coding

Eric Lippert's Blog

  • A Face Made For Email, Part Four

    Good heavens, this just keeps on happening to me.

    If you're interested in what we're musing about for future versions of C#, check out this video of Anders Hejlsberg, Scott Wiltamuth, Paul Vick, Mads Torgersen, Matt Warren, Jim Hugunin and, off in one corner, me.

    Working with this caliber of people every day is a huge part of what makes this the best job ever.

    http://channel9.msdn.com/posts/Charles/C-40-Meet-the-Design-Team/

     

  • Customer Service Is Not Rocket Science, Part Two

    I find it irritating, but not surprising, when I get absurdly bad customer service from a business whose business model is based on volume and high margins. But I find it quite surprising, and indeed, greatly amusing, to get absurdly bad customer service from a business whose business model is entirely based on quality of service.

    This was so amusing to me that I thought I would share it with you all. A little fun for the first work day of the summer.

    I have difficulty keeping up with my lawn and gardening. I decided to go to an internet based company which recommends and rates home service contractors. We'll call them "Referral Service". I used "Referral Service" to find a lawn guy in my area, who we will refer to as "Lawn Guy".

    After arranging an appointment time and a reasonable price, I got a phone call from "Referral Service". The guy on the phone was polite, enthusiastic, and asked me all kinds of questions about what other projects I might have going on around my house that they could help me with. I told the guy that yes, I have lots of projects -- I have a fence that needs rebuilding, I have some rooms that need painting, I have an unfinished renovation project in my basement. I told the guy that I would likely use their web site in the fall to arrange contractors for these various other projects.

    Fine. Seems like everyone is pretty competent so far. But as it happened, Lawn Guy didn't work out, for reasons which will rapidly become apparent.

    A few days later I got an email -- clearly a form letter -- from Referral Service, asking me to fill out a form to say how well Lawn Guy did. I thought about it for a minute and realized that my concern was sufficiently outside of the normal experience that I was not comfortable just filling out a number on a Likert Scale. I wanted to clearly express to Referral Service exactly what my problem was so that they could deal with it appropriately. Here are the emails, slightly reformatted to make them easier to read in this medium, and with names changed to protect the guilty.

    *****

    Good afternoon [Referral Service]

    [Lawn Guy] made rude and deprecating comments about Poles to my housemate on his first day on the job. That he did not know that she was of Polish descent is hardly an excuse. I've fired him.

    Eric Lippert

    *****

    Dear Eric,

    Thank you for your email. Ratings & Reviews are perhaps the most important service we offer to our members. This area of our web site includes valuable word-of-mouth feedback from [Referral Service] customers. For all requests you place with [Referral Service], you'll be asked to submit Ratings & Reviews for the service professionals you are presented. When you do, you not only help other people make their choice, but you contribute to the overall quality of service we offer.

    To submit a Rating and Review please visit our site [there then follows a list of the nine different things that you have to click on to submit a review]

    Please let us know if there is anything else we can do, and we hope you tell others about our service. We look forward to helping you with all of your future home improvement needs.
    Best Regards,

    Andrew Throatwobbler
    [Referral Service]

    *****

    Mr. Throatwobbler,

    You asked that I let you know if there is anything else we could do. What you could have done is respond to my concern with something other than a canned form letter -- a letter, which I note, asks me to do work for your benefit. My job is not to provide content for your website; I don't care a bit about your website. I care about my lawn. If you want my business you really ought to be concentrating on that.

    You said that you hoped I would tell others about your service. No, no, you don't. You ought to hope very much that I do not tell others. Responding to a report of a gross, offensive, personal insult with a form letter is the very depth of poor service.

    Eric

    *****

    Dear Eric,

    Thank you for your email and reply; we appreciate your taking the time to provide your feedback. Please know that we take these matters seriously and this is not the level of professionalism we have come to expect of our professionals. By providing your rating it additionally notates their account and should we ever notice a negative trend we do reserve the right to remove them from the service.

    Thank you for your time and for using [Referral Service].

    Regards,
    Nicole Otterbach

    *****

    Ms. Otterbach,

    I care not a bit about your policies for deciding who gets to be part of your service or not. I already told you that I don't care about that, and yet you go on about it.

    You seem to have completely forgotten that you are in the business of recommending someone to mow my lawn! A lawn which is now going unmowed because I had to fire the racist you sent me after he insulted my housemate to her face.

    The smart thing to do would have been to concentrate on the fact that you have failed utterly in what ought to be your core competency -- supplying me with a competent lawn care person. Instead, you've concentrated on sending me emails about how my doing work for free on your rating system benefits your business.

    Eric Lippert

    *****

    Eric,

    Thank you for your response. Both Ms. Otterbach and myself have expressed to you the improtance [sic] of submitting a rating on this service provider. If you submit a negative review about this service provider, then it will be followed up by our Ratings department and the necessary steps will take place. At this time, I can not locate your account in our network and am unable to see if you have this submitted. If not, please do so. Thank you.

    Andrew Throatwobbler

    *****

    Mr. Throatwobbler,

    Yes, you certainly have expressed that this is important to you, just as I have expressed that it is not important to me. Yet you continue to concentrate on it! An important principle of customer service, or, for that matter, life in general, is "when you find yourself in a hole, stop digging", yet you continue to dig. I find this interesting from a psychological point of view. (However, though interesting, psychological musings are certainly not getting my lawn mowed.)

    As for the "necessary steps" -- my filling out a form is not a precondition of the necessary steps; I think you and I have a different view on what is "necessary" in such a situation. When I was in the service industry I was taught that the necessary steps for dealing with an upset, angry or disappointed customer were these three:

    1) First, and most important, express regret that the problem occured, particularly with regard to the actions you took that precipitated the situation. As yet, no one in your organization has said that you in any way regret referring a boorish racist who insulted my housemate, a racist whom I had the unpleasant and upsetting task of firing. I would have thought that you would sincerely regret that your recommendation caused several people pain and distress, but apparently not.

    2) Second, take this as an opportunity put a good light on the organization by distancing yourself from the situation. Ms. Otterbach did that, by saying that this is not the level of professionalism you would expect.

    3) Third, take steps to make the customer's problem better. You've certainly not done that. You've concentrated solely on your issues -- your web site content, your rating system, your policies, your strange inability to find a customer in your own system. I really don't care whether you continue to refer Lawn Guy to others; that is not my problem. That's your problem. I've told you repeatedly that I don't care about that; I care about getting my lawn mowed. And yet you keep on telling me about your rating system.

    One out of three is not good, particularly since none of these steps are difficult.

    Studies have shown that customers who have a negative experience that is dealt with rapidly and respectfully have a higher opinion of an organization than customers who have never had any problems! Customers who are angry, upset or disappointed are actually a gold mine for you, because when you go the extra mile to solve their problems, they turn around and advertise your business to others for free.

    Of course, the opposite is also true. Customers who report a problem and get back form letters, no apology, and repeated requests to do work that is to your benefit, not theirs, also tell others.

    However, one good thing that has come out of this is that I now have an article for my blog.

    Cheers,
    Eric Lippert

    *****

    I haven't heard back from Mr. Throatwobbler yet. I'm in so much suspense, as I am sure are you all! Will he and Ms. Otterbach continue to attempt to tag team a former customer into submitting to their bureaucratic policy machinery? If they ever get back to me I'll post an update.

  • Method Type Inference Changes, Part One

    I want to start this by discussing the purpose of method type inference, and clearing up some potential misunderstandings about type inference errors.

    First off though, a brief note on nomenclature. Throughout this series when I say "type inference" I mean "method type inference", not any of the other forms of type inference we have in C# 3.0. (Implicitly typed locals, implicitly typed arrays, and so on.)

    The purpose of type inference is to allow generic methods to be called without explicitly specifying the generic types. For example, if you have a generic method

    static T Largest<T>(IEnumerable<T> list) where T : IComparable<T> { ... }

    then it would be nice to be able to say

    decimal maxPrice = Largest(prices);

    without having to say redundant information:

    decimal maxPrice = Largest<decimal>(prices);

    This was useful in C# 2.0, but with the advent of extension methods and query comprehensions in C# 3.0, it is downright vital. If we made Largest into an extension method then clearly you want to be able to say

    from customer in customers
    from invoices in customers.invoices
    select invoices.prices.Largest();

    and not have to insert this ugly mechanism information into your lovely semantic query:

    from customer in customers
    from invoices in customers.invoices
    select invoices.prices.Largest<decimal>();

    When you call a method without an explicit argument list, overload resolution must figure out which method you meant to call. The way type inference works with overload resolution is that all methods of that name are put into a pool called the candidate set. If any of them are generic, we run a type inference algorithm to see if the generic type arguments can be inferred for the method. If they can, then the constructed generic method stays in the candidate set; if inference fails then it is removed. Then all methods which are inapplicable -- that is, the arguments cannot be converted to the parameter types -- are removed. Of the remaining applicable candidates, either a unique best candidate is chosen. If a unique best candidate cannot be identified then overload resolution fails.

    Note that type inference failure is not actually an error in of itself. However, type inference failure (or success!) might cause real errors downstream in a number of ways. For example:

    • if type inference fails and the candidate set becomes empty as a result, then overload resolution fails
    • if type inference fails when the user intended the generic method to be the chosen one, and the remaining candidates have no unique best member, then overload resolution fails
    • if type inference succeeds when it wasn't expected to then there might be more methods in the candidate set than expected. If the inferred method ties for "bestness" with another candidate then overload resolution fails. (The tiebreaker rules in overload resolution are designed to avoid this scenario, but it is still possible in contrived situations.)

    This then brings up the question of what error message to display when overload resolution fails. In C# 2.0 we have a heuristic which tries to detect when overload resolution failed because of the first case (which is the most common of the three cases.) In those cases it gives an error message that says "type inference failed" rather than "overload resolution failed". Even though the actual error as far as the language specification is concerned is that the candidate set contained no unique best applicable member, the user typically thinks of the cause of the error as being type inference failure in many cases, so we try to do a good job of reporting that.

    It works pretty well in C# 2.0, but we struggled with this a lot in C# 3.0. Suppose you end up in a situation where you have a query:

    IEnumerable<Customer> customers = ...;
    var firstNames = from c in customers select c.FristName;

    Oops. That is going to be translated into customers.Select(c=>c.FristName). There are no methods named "Select" on IEnumerable<Customer>, we look for extension methods and find Enumerable.Select<T, R>(this IEnumerable<T> list, Func<T, R> selector). Type inference infers T as Customer, but cannot figure out what R is because Customer does not have a property FristName. Therefore type inference fails, and therefore overload resolution fails, and therefore our heuristic takes over and gives the error that type inference failed on Select.

    I think you would agree that the user does not think of the error in this program as being a failure of overload resolution or type inference; they think that the failure is that there's a typo in one clause of the query. Wes did quite a bit of work on the heuristics to go back one step further, and report why type inference failed; because the body of the lambda could not be successfully bound.

    So that's one change to type inference between C# 2.0 and C# 3.0, but I seem to have gotten ahead of myself somewhat. Next time we'll take a closer look at how the mechanism of type inference works in C# 2.0.

  • An apology

    I posted an article recently entitled "The Managed Languages Team is Hiring", and the response I received was nigh overwhelming. Dozens of resumes and CVs from industry professionals, researchers and students poured in. It was very gratifying, and I was excited to get so many great leads.

    I collected up all of those resumes for sorting by our hiring managers, only to be told that it had come down from on high mere hours before that no, actually, we've now met our hiring goals. We've been instructed to fill up any additional positions that come open due to attrition, but do not grow the teams to be larger; after a long recruiting push we have enough people to meet our goals for the next round of languages and tools for .NET.

    I therefore want to apologize to all of you who sent me resumes and wrote great cover letters, some of them quite long, heartfelt and interesting. I know that you put work into them, and appreciate that. Obviously I would never have gotten your (and my!) hopes up had I known that in fact the strong push that I'd been getting from management to find more candidates was about to suddenly be reversed.

    I do not mean for a moment to be discouraging about working at Microsoft; the company as a whole still has a great many positions available. And it is not like we are just going to throw all those resumes away; leads are valuable. The ones from college students will be sent on to our college recruiting specialists, and so on. But if you want a job at Microsoft, your best bet is still to look at our career site and apply for specific positions.

    And finally, a great many people asked me whether we hire foreign nationals for our development center in the United States. Given that I am Canadian and I work directly with people from India, China, Israel, Denmark and a great many other places, it seems we have done in the past.

    I asked our human resources staffing consultant about the present situation, and she said that we at present have no legal difficulty hiring people from Canada, Mexico, Chile, Singapore and Australia. It is somewhat difficult to hire foreign nationals from other countries at the moment because federal law limits the number of visas we can obtain and they are running short. However, apparently there will be more visas available in October.

    Obviously I am not an expert in immigration law; if you have further questions about the supply and demand for visas, I suggest that you take it up with an immigration lawyer, or find a conveniently located member of the House of Representatives or Senate and ask them what the situation is and how it got that way.

    And finally, of course Redmond is not the only place where Microsoft has development centers. We have dev work going on internationally in Bejing, Copenhagen, Cairo, Aachen, Hyderabad, Dublin, Herzelia, Haifa, Zurich and Cambridge, and other subsidiaries pretty much everywhere for non-development work.

  • Method Type Inference Changes, Part Zero

    Back in November I wrote a bit about a corner case in method type inference which does not work as expected or as specified in C# 3.0. A number of people made blog comments, sent me mail, and entered "Connect" issues with additional problems and ideas for how we could improve this algorithm. (Particular thanks to nikov for his many fascinating and detailed Connect reports.) As a result, as soon as I had time I embarked upon a detailed review of the method type inference specification and the implementation.

    The good news is that this resulted in a complete overhaul of both the implementation and the specification; they are now consistent with each other and to the best of my knowledge, correct. Furthermore, the principle concern of commenters on my earlier article has been dealt with: return type inference from a method group to the return type of a delegate type will be legal, but only when the delegate type's input parameters are all completely known. This eliminates the "chicken and egg" problem I discussed in November, whereby overload resolution on the method group must consume the very same unfixed formal parameter types that it is attempting to infer.

    The bad news is that these changes to the implementation will not make it in to the service release of C# 3.0; they will have to wait for a later revision of the compiler. (It is not yet clear to me whether or not the changes to the specification will make it into the next edition of the published specification.)

    This is one of the most complex areas of the specification and implementation; I'd like to spend some time in this blog going over the specification in detail and explaining where we got things subtly wrong, and how we intend to fix it.

    In this series I want to hit on the following points:

    • What did method type inference look like in C# 2.0? Why was it inadequate for LINQ?
    • How did we attempt to modify and ultimately rewrite the specification for C# 3.0?
    • Where did we go subtly wrong in the specification and the implementation?

    Next time, we'll get started with a look back at C# 2.0.

  • Precedence vs Associativity vs Order

    Raymond has written about this, I have written about Raymond writing about it, but I still frequently get questions from people who are unclear on the difference between precedence, associativity and evaluation order.

    I suspect that this confusion arises from the difference between how most people are trained to evaluate arithmetical expressions versus how compilers generate code to evaluate arithmetical expressions. As a child it was drilled into me that the way to evaluate an arithmetical expression was to recursively apply the PEDMAS rule. That is, evaluate anything in parentheses first. Then evaluate any exponentiations. Then divisions, multiplications, additions and subtractions, in that order. So if you had 4 x 5 x (20 - 12 / 3) you start by evaluating what's in parentheses: 20 - 12 / 3. In there, there are no parens or exponents, so start with the division. Replace 12 / 3 with 4 to get 20 - 4. Then evaluate the subtraction to get 16. Now we have the value for the parens, and we are down to 4 x 5 x 16. Evaluate one of the multiplications -- but wait, we do not know what order to evaluate the multiplications in. But we can do it in any old order, so lets say 5 x 16 is 80, so we have 4 x 80, which is 320, done.

    You'll notice that the algorithm that I was taught emphasizes that you do the work on whatever is in the deepest set of parentheses first, no matter what. Many people believe that arithmetical expressions in programming languages work the same way. They do not.

    Rather, in programming languages, parentheses indicate how the results of evaluations are combined, but not necessarily the order in which the calculations are carried out. In languages with no side effects, the order of evaluation is irrelevant. But in languages where side effects might occur, order becomes relevant.

    The evaluation of an arithmetical expression is controlled by three sets of rules: precedence rules, associativity rules, and order rules.

    Precedence rules describe how an underparenthesized expression should be parenthesized when the expression mixes different kinds of operators. For example, multiplication is of higher precedence than addition, so 2 + 3 x 4 is equivalent to 2 + (3 x 4), not (2 + 3) x 4.

    Associativity rules describe how an underparenthesized expression should be parenthesized when the expression has a bunch of the same kind of operator. For example, addition is associative from left to right, so a + b + c is equivalent to (a + b) + c, not a + (b + c). In ordinary arithmetic, these two expressions always give the same result; in computer arithmetic, they do not necessarily. (As an exercise can you find values for a, b, c such that (a + b) + c is unequal to a + (b + c) in C#?)

    Now the confusing one.

    Order of evaluation rules describe the order in which each operand in an expression is evaluated. The parentheses just describe how the results are grouped together; "do the parentheses first" is not a rule of C#. Rather, the rule in C# is "evaluate each subexpression strictly left to right".

    The expression F() + G() * H() is equivalent to F() + (G() * H()), but C# does NOT evaluate G() * H() before F(). Rather, this is equivalent to:

    temp1 = F();
    temp2 = G();
    temp3 = H();
    temp4 = temp2 * temp3;
    result = temp1 + temp4;

    Another way to look at it is that the rule in C# is not "do the parentheses first", but rather to parenthesize everything then recursively apply the rule "evaluate the left side, then evaluate the right side, then perform the operation".

    This is not a rule of C++. In C++, F(), G() and H() can be evaluated in whatever order the compiler chooses, so long as it combines the results in the right way. A legal C++ compiler might do left to right, right to left, parentheses first, whatever the compiler writer felt like.

    The way this topic usually comes up is when someone has an expression chock full of side effects -- assignments, increments, decrements, pointer stores and so on, which they are attempting to convert from C++ to C#, and report the "bug" in the C# compiler to me that C# does not follow the "rules" of C++. Which is ironic, because since there are not actually any rules in C++ about order of evaluation between two sequence points. Thus the bug actually is that their code was never portable C++, and only worked because the code author happened to know (or guess) what ordering the compiler writer chose.

  • Method Hiding Apologia

    Here's some back-and-forth from an email conversation I had with a user a while back.

    Why should one avoid method hiding? 

    If there were no advantages and only disadvantages then we would not have added it to the language in the first place. C# implements hiding because hiding is frequently useful. 
     
    I therefore deny the premise of the question. One should not avoid method hiding if it is the right thing to do.

    But I find method hiding confusing. It lets derived types appear to break the contracts of base types. If a derived type D hides a method M on base class B because D.M does something different than B.M, shouldn't it have a different name?

    That sounds remarkably like a good answer to your original question.

    However, method hiding is for exactly those times when you need to have two things to have the same name but different behaviour.
     
    Obviously that is not a pleasant situation to be in. I agree with you that it is almost always preferable to have different things have different names. However, I can think of a few situations in which it's desirable. Consider this real-world example:
     
    interface IEnumerable<T> : IEnumerable {
      new IEnumerator<T> GetEnumerator();
    }
     
    Is there a better name than GetEnumerator? That's what it does: it gets an enumerator. And in this case, you want to suppress the usage of the base type's non-generic GetEnumerator as much as possible; this version is intended to fully replace the non-generic version.

    Of course, if C# had method return type covariance, this would not have to be a new method. That gives us a larger good reason for method hiding; it allows for something like return type covariance in a language that does not have such a feature.

    I suppose that makes sense. Could you give some other examples of valid usage?

    Sure, but I will have to digress in a prolix manner first.

    We want to write computer programs which solve real-world problems, and therefore we want to design languages which enable developers to model their real-world problems in natural, flexible and intuitive ways. We want to take problem solving techniques which work well in the non-computer realm and enable developers to use the real-world intuitions and skills they’ve learned in their program.

    One of the techniques that we developed millennia ago to solve problems is organization of objects into hierarchies. Hierarchies are often imperfect – there are the occasional platypuses which crop up and resist easy classification. But hierarchies are such a powerful and useful tool that we use them, despite their flaws, to help organize the world’s data and solve real problems.

    Thus, it should be clear that class-based inheritance exists in programming languages because we wish to enable developers to naturally and easily write programs which model problems solved by hierarchies.

    So here’s the rub: in a world where objects fit into a hierarchy, who gets to decide how to manipulate that object based on its position in the hierarchy?  Does the object get to decide (at runtime), or does the code doing the manipulation get to decide (at compile time)? 

    The former is called “virtual dispatch”, the latter “non-virtual dispatch”.  Which you choose to use depends entirely upon the problem being modeled. It’s not like one of them is absolutely morally better than the other. The better one is the one which models the real-world problem better.

    Many problems – probably the majority of problems in this space – are best modeled by letting the object decide how it is to be treated.  When you call Feed on an instance of Animal, let the instance decide what happens based on whether the instance is a Squid or a Zebra. Do a virtual dispatch at runtime. But it would be an error to say that because most problems are modeled this way, that the language ought not to allow modeling the problem any other way. Sometimes it is best for the caller to decide how the object is treated, because the caller has more information.

    For example, when I was a teenager my father owned a restaurant. This was at the time that the Conservative government in Canada introduced a highly unpopular value-added sales tax on goods and services called, unimaginatively enough, the Goods and Services Tax.  The GST rules were roundly criticized as being insanely complicated. No government wants to be known as “the government that increased the price of food for poor people”, so grocery items were exempted from the GST. But what qualified as a “grocery item”? That had a whole other complex set of rules.

    A cake sold in a grocery store, no GST. The exact same cake, sliced and plated in my father’s restaurant, GST. The exact same cake, NOT sliced, sold whole in the restaurant, no GST. A muffin in a grocery store, no GST. The same muffin in my father’s restaurant, GST. A box of six muffins sold in the restaurant, no GST.

    I gather that in the last twenty years the tax has been rationalized somewhat. That’s not my point. My point is that this is a case where what the object is (virtual dispatch) is only one factor in how it behaves with respect to taxes. An equally important factor is how the object is being classified right now (non-virtual dispatch).

    How might we model this in a programming language? There are lots of ways, and we could certainly argue about which is best. C# allows you the flexibility to come up with a variety of different designs and decide for yourself which models your problem best. One way in which we could reasonably model this problem would be to have a hierarchy:

    abstract class Food {
        public decimal TaxRate { get { return 7.0m;} }
    }
    abstract class Grocery : Food {
        new public decimal TaxRate { get { return 0.0m; } }
    }
    class Cake : Grocery {
        new public decimal TaxRate { get { return 7.0m; } }
    }

    And so on. We could continue to complexify this to model the tax rules better. Now when I have a variable of type Cake, it’s tax rate is 7%. When I have the same cake stuck into a variable of type Grocery, its tax rate is 0%. That is, what the object “really” is at runtime is less relevant to the problem at hand then how I am presently classifying it.

    One might make the argument that this is a violation of the Single Responsibility Principle, that the tax logic should be in a class of its own, that groceryness should be a flag and not a part of the type system, that really what we need is both a Food and a TransactionContext hierarchy, blah blah blah. I am sure that we could come up with a completely different hierarchy of objects which did not require hiding, and that other hierarchy might even be “better” in some ways. We seem to be badly conflating mechanism with policy here, which is worrisome.

    But that's not my point. My point is that in C# we want to give you the flexibility to model problems this way if you choose to, if you believe that this is the best way to model them. And I think that’s a good thing.

    I wrote a couple of articles about how we designed this same feature into JScript .NET. It is a language greatly complicated by its dynamic nature, so the design decisions became correspondingly harder. See

    http://blogs.msdn.com/ericlippert/archive/2004/06/07/150367.aspx
    http://blogs.msdn.com/ericlippert/archive/2004/06/08/151209.aspx

    The comments are particularly useful in these articles as well.

  • A Generic Constraint Question

    Here's a question I get fairly frequently: the user desires to create a generic type Bar<T> constrained such that T is guaranteed to be a Foo<U>, for some U.

    The way they usually try to write this is

    class Bar<T>  where T : Foo<T> {...

    which of course then does not work. The user discovers this when they type

     new Bar<Foo<string>>()

    and get an error stating that the constraint has been violated. Which it certainly has. The constraint says that T must be Foo<T>, so therefore string must be Foo<string>, which clearly it is not.

    There are a few ways to solve this problem. One is to simply introduce a new type parameter:

     class Bar<T, U> where T : Foo<U>

    this means that you now have some redundancy:

    new Bar<Foo<string>, string>()

    But let's stop and think for a moment about what the user desired in the first place. Suppose there were a way to specify what we sometimes call a "mumble type" on the C# design team:

    class Bar<T>  where T : Foo<?> {...

    Think about the consequences of that on the caller side; the compiler could verify that new Bar<Foo<string>>() was legal. But what could the compiler verify on the callee side?

    class Bar<T>  where T : Foo<?> {
       public void Blah(T t) {
        t.

    t dot what? We don't know anything about T except that it is Foo<something>. What methods, properties, fields or events of Foo could we access? Only those that in no way consume the generic type, which seems like precious little. If Foo had a whole lot of stuff that wasn't generic, why was it made generic in the first place?

    But this then leads to an insight; if the user owns the Foo<T> class, then they could do precisely that:

    public abstract class FooBase
    {
      private FooBase() {} // Not inheritable by anyone else
      public class Foo<U> : FooBase {...generic stuff ...}
      ... nongeneric stuff ...
    }

    public class Bar<T> where T: FooBase { ... }
    ...
    new Bar<FooBase.Foo<string>>()

    and now everyone is happy. The compiler restricts the construction of Bar to FooBase on the caller side. The compiler ensures that every runtime instance of FooBase is an instance of Foo<T>. And the compiler allows the callee side to use all the non-generic methods on FooBase.

    Personally, I would go for the first solution myself. But it's edifying to try and find additional solutions, even if they are a bit odd.

  • Reading Code Over the Telephone

    In my youth I once attended a lecture given by Brian Kernighan on the subject of code quality, which was very influential on my attitudes towards writing legible code. One of the things that Kernighan recommended was to endeavour write code that was so clear that it could be easily read over the phone and understood. Most people find code much easier to comprehend when read than when heard; if you can make it clear enough to be understood when heard, it's probably pretty clear code.

    I was reminded of this when I got the following question from a colleague by email:

    Subject: Stupid C# 3.0 lambda expression question

    How does one read the => operator?

    First off, I told my colleague that there are no stupid questions, only stupid people. No stupid people work here, so don't stress about it. This is a perfectly sensible question.

    As far as I know, we do not have an "official" line on how to read this operator over the phone. In the absense of any other context, I personally would say c=>c+1 as "see goes to see plus one". Some variations that I've heard:

    For a projection, (Customer c)=>c.Name: "customer see becomes see dot name"

    For a predicate, (Customer c)=>c.Age > 21: "customer see such that see dot age is greater than twenty-one"

    An unfortunate conflation is that the => operator looks a lot like ⇒, the "implies" operator in mathematics. Since => does not have the same semantics as ⇒, it is probably a bad idea to read => as "implies". (x⇒y would have the semantics of !x|y in C#.)

    Incidentally, it is a little known fact that VB6 and VBScript implemented the ⇒ operator with the Imp keyword and the ⇔ operator with the Eqv keyword. They disappeared in VB.NET. Where did they go? It is a mystery!

  • Mutating Readonly Structs

    Consider this program which attempts to mutate a readonly mutable struct. What happens?

    struct Mutable {
        private int x;
        public int Mutate() {
            this.x = this.x + 1;
            return this.x;
        }
    }

    class Test {
        public readonly Mutable m = new Mutable();
        static void Main(string[] args) {
            Test t = new Test();
            System.Console.WriteLine(t.m.Mutate());
            System.Console.WriteLine(t.m.Mutate());
            System.Console.WriteLine(t.m.Mutate());
        }
    }

    There are a number of things this program could do. Does it:

    1) Print 1, 2, 3 -- because m is readonly, but the "readonly" only applies to m, not to its contents.
    2) Print 0, 0, 0 -- because m is readonly, x cannot be changed. It always has its default value of zero.
    3) Throw an exception at runtime, when the attempt is made to mutate the contents of a readonly field.
    4) Do something else

    ?

    People are frequently surprised to learn that the answer is (4). In fact, this prints 1, 1, 1. 

    Why?

    Because, remember, accessing a value type gives you a COPY of the value. When you say t.m, you get a copy of whatever is presently stored in m. m is immutable, but the copy is not. The copy is then mutated, and the value of x in the copy is returned. But m remains untouched.

    The relevant section of the specification is 7.5.4, which states that when resolving "E.I" where E is an object and I is a field...

    ...if the field is readonly and the reference occurs outside an instance constructor of the class in which the field is declared, then the result is a value, namely the value of the field I in the object referenced by E.

    The important word here is that the result is the value of the field, not the variable associated with the field. Readonly fields are not variables outside of the constructor. (The initializer here is considered to be inside the constructor; see my earlier post on that subject.)

    Great. What about that second dot, as in ".Mutate()"?  We look at section 7.4.4 to find out how to invoke E.M():

    If E is not classified as a variable, then a temporary local variable of E's type is created and the value of E is assigned to that variable. E is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as this within M, but not in any other way. Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.

    And there you go. Value semantics are tricky!

    This is yet another reason why mutable value types are evil. Try to always make value types immutable.

  • Trivial Projections Are (Usually) Optimized Away

    OK, computers aren't entirely dumb when it comes to LINQ. Here's an example of a place where we're a bit smarter.

    Consider the following query:

    IEnumerable<int> query = from n in number_array orderby n select n;

    Does this get transformed by the compiler into

    IEnumerable<int> query = number_array.OrderBy(n => n);

    or

    IEnumerable<int> query = number_array.OrderBy(n => n).Select( n => n );

    ?

    This question caused tremendous debate amongst the members of the C# design and implementation teams. I am quite happy with the compromise we achieved.

    The sections of the C# specification which cover this topic are sections 7.15.2.3, and 7.15.2.5:

    ***

    A query expression of the form “from x in e select v” is translated into “( e ) . Select ( x => v )” except when v is the identifier x, the translation is simply “( e )”

    A query expression of the form “from x in e select x” is translated into “( e ) . Select ( x => x )”.

    A degenerate query expression is one that trivially selects the elements of the source. A later phase of the translation removes degenerate queries introduced by other translation steps by replacing them with their source. It is important however to ensure that the result of a query expression is never the source object itself, as that would reveal the type and identity of the source to the client of the query. Therefore this step protects degenerate queries written directly in source code by explicitly calling Select on the source. It is then up to the implementers of Select and other query operators to ensure that these methods never return the source object itself.

    ***

    It might sound like the second statement contradicts the first. The first statement says that the “.Select(x=>v)” is omitted when “v” is “x”, and the second statement says that no, the “.Select(x=>x)” is in fact generated.

    The spec is not particularly clear on this point, I agree, though the explanatory text about “degenerate query expressions” does help clear it up somewhat. Basically what we are saying here is that the optimization to remove degenerate selects is only performed on the result of an earlier query translation.

    Some examples will help.

    If you have from n in number_array orderby n select n then this is first translated into from n in (number_array).OrderBy(n=>n) select n and then, since the selector is the same as the range variable, this is translated into ((number_array).OrderBy(n=>n)).

    We optimize away the Select(n=>n) because from n in (number_array).OrderBy(n=>n) select n is the intermediate result of an earlier query translation.

    If you had from n in number_array select n then this would be considered a degenerate query expression requiring a Select. We cannot simply translate it into (number_array) because then you could assign that to a variable and have referential identity with the collection. The result of the query should not “leak” information about the collection out; it should just be an enumerable object of the appropriate type, and not have an identity relationship. Therefore, we take the performance hit and protect the identity of the collection by emitting a degenerate query translation (number_array).Select(n=>n).

    This is important because the collection might be a mutable collection. One does not expect that the result of a query over that collection could be used to mutate the collection! The query results should be read-only.

    We do not need to append the degenerate Select(n=>n) when you have an “orderby” in there because the OrderBy(n=>n) call already protects the identity of the original collection. This saves on the performance cost of the no-op Select.

    I think we’ve made a reasonable compromise in the case of degenerate queries. If you are crazy enough to write “from x in xs select x” when you mean “(xs)”, then you’ll have to live with the decreased performance. But if you are introducing a degenerate select at the end of a meaningful query, then it will be optimized away.

  • Computers are dumb

     

    A few short takes today, from questions I've received recently about LINQ in C# 3.0.

    The first question was "in the following code, does it really check every single non-negative integer, or does it use the knowledge that once you're beyond ten, you can stop iterating?"

    var smallNumbers = Enumerable.Range(0, int.MaxValue).Where(n => n < 10);
    foreach (int i in smallNumbers)
        Console.WriteLine(i);

    The former. You asked LINQ to Objects to apply a predicate to a sequence and that's what it does. If the sequence has two billion elements in it, it runs the predicate two billion times.

    You certainly could build your own range object which had a Where method that took an expression tree. Your Where method could then analyze the range and the expression tree in an attempt to build a new range object that iterated a smaller range. Analyzing simple predicates would be pretty easy; more complex predicates would have to be turned back into delegates.

    Of course, if it hurts when you do that, don't do that. if you already know the range you want, just restrict it in the range constructor in the first place!

    The second question was "which is the preferred coding style?" :

    if (0 != customers.Count()) ...

    vs

    if (customers.Any()) ...

    On efficiency grounds alone the latter is far preferable. Suppose you have a box which may contain between zero and two billion pennies. You want to take some action if there are any pennies in the box, but you don't care how many there are, only whether there are zero or more. In that scenario, clearly it makes no sense whatsoever to count them all. The Count method doesn't know that all you care about is whether it's going to return zero or not. You asked it to count, so it counts.

    (I am reminded of an old joke: on a bank teller's first day, he is asked to count a stack of twenties and verify that there are one hundred in the stack. He starts counting them from one pile into another: one, two, three, ... but when he gets to fifty, he stops and says to his boss "I'll stop here -- if it's right all the way up to fifty, odds are good it's right the whole way.")

    Even leaving the obvious efficiency concern aside, the latter code reads better. It more clearly reflects the intention of the programmer.

    The third question was "when iterating through a collection, I noticed that the former code is much faster than the latter code. Any insight as to why?"

    myEventLogEntryCollection.CopyTo(myEntriesArray,0);
    for(int i=0; i < myEntriesArray.Length, i++) {...}

    vs

    foreach(EventLogEntry myEntry in myEventLogEntryCollection) {...}

    This made no sense to me. Sure, there will be differences. The former code is using more memory because it makes a copy of the collection; initializing that memory is going to take significant time if the collection is large, but I could see how maybe the array access could be faster than the collection iterator in the loop. Perhaps the improvement in iteration is fast enough that for a large collection, the larger initialization cost was made up. But the user was reporting "much faster", not "slightly faster". I asked for more details.

    What exactly were the timings? Three seconds vs sixteen seconds. Holy goodness! I had figured we were talking about microseconds of difference here, not actual humans sitting around watching the clock time. What on earth was the user doing? Accessing event logs from a remote machine over a slow network.

    Aha! Now it is clear what is going on here. The iteration time is being dominated by the round trip to the remote machine; the former code makes one very expensive remote call that takes three seconds. The latter code makes hundreds of cheaper remote calls, possibly several per loop iteration, that take a fraction of a second each but which add up to more expense in the long run.

    My "insights" were therefore:

    • You can sometimes trade increased memory use for decreased time. 
    • The cost of fetching n items by making m requests might be dominated by m, not n.
    • If you don't actually describe the relevant features of the problem, it is impossible to get a correct analysis.

    I blogged about these three questions not because I got them all in the same day, but because there is a common thread to the answers: computers are dumb. They do exactly what you tell them. If you want performance savings by eliminating unnecessary work through logical deductions about what work can be eliminated, you're going to have to be the one to do make those deductions!

  • Covariance and Contravariance, Part Eleven: To infinity, but not beyond

    UPDATE: Andrew Kennedy, author of the paper I reference below, was good enough to point out some corrections and omissions, which I have addressed. Thanks Andrew!

    As I've discussed at length in this space, we are considering adding covariance and contravariance on delegate and interface types parameterized with reference types to a hypothetical future version of C#. (See my earlier articles in this series for an explanation of the proposed feature.)

    Variance is highly useful in mainstream cases, but exposes a really irksome problem in certain bizarre corner cases. Here's just one example.

    Consider a "normal" interface contravariant in its sole generic type parameter, and a "crazy" invariant interface which extends the normal interface in a strange way:

    public interface IN<in U> {}
    public interface IC<X> : IN<IN<IC<IC<X>>>> {}

    This is a bit weird, but certainly legal.

    Before I go on, I want to digress and talk a bit about why this is legal. Most people when they first see such a beast immediately say "but an interface cannot inherit from itself, that's an illegal circularity in the inheritance chain!"

    First off, no, that is not correct. Nowhere does the C# specification make this kind of inheritance illegal, and in fact, a weak form of it must be legal. You want to be able to say:

    interface INumber<X> : IComparable<INumber<X>> { ... }

    that is, you want to be able to express that one of the guarantees of the INumber<X> contract is that you can always compare one number with another one. Therefore, it must be legal to use a type's name in a type argument of a generic base type.

    However, all is not rosy. This particularly gross kind of inheritance that I give as an example is in fact illegal in the CLR, even though it is not illegal in C#. This means that it is possible to have the C# compiler generate an interface type which then cannot be loaded by the CLR. This unfortunate mismatch is troubling, and I hope in a future version of C# to make the type definition rules of C# as strict or stricter than those of the CLR. Until then, if it hurts when you do that, don't do it.

    Second, unfortunately, the C# compiler presently has numerous bugs in its cycle detector such that sometimes things which kinda look like cycles but are in fact not cycles are flagged as cycle errors. This just makes it all the more difficult for people to understand what is a legal cycle and what isn't. For example, the compiler today will incorrectly report that this is an illegal base class cycle, even though it clearly is not:

    public class November<T> {}
    public class Romeo : November<Romeo.Sierra.Tango> {
       public class Sierra {
           public class Tango {}
        }
    }

    I have devised a new (and I believe correct!) cycle detection algorithm implementation, but unfortunately it will not make it into the service release of the C# 3 compiler. It will have to wait for a hypothetical future release. I hope to address the problem of bringing the legal type checker into line with the CLR at the same time.

    Anyway, back to the subject at hand: crazy variance. We have the interfaces defined as above, and then give the compiler a little puzzle to solve:

    IC<double> bar = whatever;
    IN<IC<string>> foo = bar;  // Is this assignment legal?

    I am about to get into a morass of impossible-to-read generic names, so to make it easier on all of us, I am going to from now on abbreviate IN<IC<string>> as NCS. IC<double> will be abbreviated as CD. You get the idea I'm sure.

    Similarly, I will notate "is convertible to by implicit reference conversion" by a right-pointing arrow. So the question at hand is true or false: CD→NCS ?

    Well, let’s see. Clearly CD does not go to NCS directly. But (the compiler reasons) maybe CD’s base type does.

    CD’s base type is NNCCD. Does NNCCD→NCS? Well, N is contravariant in its parameter so therefore this boils down to the question, does CS→NCCD ?

    Clearly not directly. But perhaps CS has a base type which goes to NCCD. The base type of CS is NNCCS. So now we have the question does NNCCS→NCCD ?

    Well, N is contravariant in its parameter, so this boils down to the question does CCD→NCCS ?

    Let’s pause and reflect a moment here.

    The compiler has “reduced” the problem of determining the truth of CD→NCS to the problem of determining the truth of CCD→NCCS! If we keep on “reducing” like this then we’ll get to CCCD→NCCCS, CCCCD→NCCCCS, and so on.

    I have a prototype C# compiler which implements variance – if you try this, it says “fatal error, an expression is too complex to compile”.

    I considered implementing an algorithm that is smarter about determining convertibility; the paper I reference below has such an algorithm. (Fortunately, the C# type system is weak enough that determining convertibility of complex types is NOT equivalent to the halting problem; we can find these bogus situations both in principle and in practice. Interestingly, there are type systems in which this problem is equivalent to the halting problem, and type systems for which the computability of convertibility is still an open question.) However, given that we have many other higher priorities, it’s easier to just let the compiler run out of stack space and have a fatal error. These are not realistic scenarios from which we must sensibly recover.

    This is just a taste of some of the ways that the type system gets weird. To get a far more in-depth treatment of this subject, you should read this excellent Microsoft Research paper

  • Protected Semantics, Part Five: More on immutability

    I asked a second follow-up question back in Part Two:

    Suppose you wanted to make this hierarchy an immutable collection, where "Add" and "Remove" returned new collections rather than mutating the existing collection. How would you represent the parenting relationship?

    The short answer is "I wouldn't". First off, it sounds an awful lot like what is intended to be represented here is some sort of highly mutable system, where objects are being added and removed from containers. It might be more sensible to represent that thing using mutable data structures rather than immutable data structures.

    If you did want to represent the thing with immutable data structures, then I would think twice before attempting to represent parenting at all. Here's why: when you have an immutable tree structure without parents, and you want to make a new tree structure that is based on an old one, typically the only stuff you have to change is from the edited node on up to the root node; usually hierarchies are shallow, so this is not very many nodes.

    But this logically implies that the root node changes on every edit. If the root node changes, then all of its children have a new parent, and all of their children then have a new parent... and you've just rewritten the entire tree structure, not just a few nodes.

    Now, it is often highly convenient to know what the parent of a given node is. To achieve that, what I'd probably do is try to not need to query the parent of any node until I was at a point where I wanted to ask about a lot of parents, and the tree was likely to remain unedited while I was asking. A quick visitor pass that builds up a hash table mapping every node to its parent would be easy enough to write, and then the parent table could be discarded and its memory reclaimed once I was done.

  • Protected Member Access, Part Four

    In Part Two I asked a couple of follow-up questions, the first of which was:

    Suppose you were a hostile third party and you wanted to mess up the parenting invariant. Clearly, if you are sufficiently trusted, you can always use private reflection or unsafe code to muck around with the state directly, so that's not a very interesting attack. Any other bright ideas come to mind for ways that this code is vulnerable to tampering?

    Before I get into some ideas for attacks, I want to re-emphasize the bit in the middle there: attacks which presuppose that the attacker is already an administrator on your machine are not interesting attacks. A number of people came up with attacks involving the attacker being able to run debuggers or even replace the entire CLR with a custom-built runtime.

    Since there is no point in protecting code from hostile code which is already stronger than the security system itself, it's not very interesting to consider such attacks. The interesting question is how to protect yourself against hostile code which is weaker than the code you are attempting to protect. That's what attackers want to do -- they want to leverage vulnerabilities in your strong code to turn their weak code into strong code. If they already have strong code, they don't need to lure your code into doing something, they can just do it directly.

    Anyway, that said, there are a number of flaws in my proposed implementation from a security perspective:

    • Eamon Nerbonne pointed out the implementation is not in the slightest thread-safe; hostile code which wished to mess up the parenting invariant could run adds and removes on the same objects on multiple threads at once, and when the smoke clears, pretty much any weird parenting arrangement you care to name could be the case.
    • Matt pointed out that the implementation relies upon a correct implementation of hashing/equality. If a hostile or buggy object provides an implementation of hashing which is inconsistent with its implementation of equality, it is possible to put stuff into a hash set and never get it back out again. The parenting invariant in this implementation relies upon being able to use hash sets correctly.
    • Jon Skeet pointed out that the implementation has no protections against parenting relationships which are locally sensible but not globally sensible, like a box containing itself.

    All of these vulnerabilities could of course be mitigated without too much difficulty; the trick is in remembering to look for the vulnerability in the first place!

    Getting the parenting relationship consistently acyclic is an interesting algorithmic problem. There are numerous ways to do it. Two sketches for your consideration:

    1) Every time you add an item to a container, search the parent list of the container for the item. Hopefully containment relationships are shallow and this is not too expensive. 

    2) Only allow adding an item to a container if the item and the container are different but are themselves in the same immediate container. If you have a box in a bag, and you want to put a ball in the box, first put it in the bag, and then in the box. 

    Next time, some thoughts on immutability and parenting relationships.

More Posts Next page »

This Blog

Syndication


© 2008 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker