<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Fabulous Adventures In Coding : Rants</title><link>http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx</link><description>Tags: Rants</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Queueing Theory In Action, plus, frogs</title><link>http://blogs.msdn.com/ericlippert/archive/2009/08/20/queueing-theory-in-action-plus-frogs.aspx</link><pubDate>Thu, 20 Aug 2009 16:32:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9872541</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>33</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/9872541.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=9872541</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;Well that was a lovely vacation. It got off to a poor start but then it improved dramatically.&lt;/P&gt;
&lt;P&gt;Suppose you've got an "entrance" that is producing some largish number of "customers" on some schedule. You've got a bunch of "servers" who are handling the customer requests. Once a customer request is satisfied, the customer leaves through an "exit". What happens when there are more customers arriving in quick succession than there are available servers to serve them? This is the domain of &lt;STRONG&gt;analytic queueing theory&lt;/STRONG&gt;; this theory is germane to a great many human and technical problems, from obtaining a double cheeseburger,&amp;nbsp;onion rings and a large... orange... drink... to routing telephone calls over satellite networks.&lt;/P&gt;
&lt;P&gt;(Some of you might be wondering what on earth&amp;nbsp;the first two paragraphs have to do with each other. It'll come together eventually, I promise.)&lt;/P&gt;
&lt;P&gt;An interesting example of two different queue servicing algorithms is exemplified by two popular fast food restaurant chains. At restaurant "M", if there are, say, four cashiers then there are four queues. Customers arrive, choose a queue&amp;nbsp;and wait. At restaurant "W", there is one long serpentine queue; when a cashier becomes available, the person at the front of the line goes to that cashier.&lt;/P&gt;
&lt;P&gt;The principle downside of the W system is that the&amp;nbsp;single queue&amp;nbsp;&lt;EM&gt;looks&lt;/EM&gt; like it will take&amp;nbsp;much longer than four short queues in the M system, which can be daunting. But by almost every relevant objective metric, by almost every relevant social factor, and in almost every common real-world business scenario the W system is preferable:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The W system &lt;STRONG&gt;requires no customer to make a choice based on incomplete information&lt;/STRONG&gt;; the M system basically presents the customer with a roll of the dice. Which queue is fastest? It depends not just on the competence of the cashier, but on whether the transactions pending in&amp;nbsp;a given&amp;nbsp;queue are simple or complex.&lt;/LI&gt;
&lt;LI&gt;Suppose you are in the M system and two queues -- yours, and the one beside you -- are being serviced at approximately the same rate on average. It is entirely possible and indeed, perhaps common, that even though both queues are in the long run moving at the same rate, that due to sudden starts and stops in both queues you will&amp;nbsp;&lt;EM&gt;perceive&lt;/EM&gt; that you are spending more time "being passed" by the people next to you than "passing" them. It is quite possible for people in &lt;EM&gt;both&lt;/EM&gt; queues to have this perception simultaneously! &lt;STRONG&gt;Everyone feels like they've chosen the wrong queue&lt;/STRONG&gt;, even if there is no "wrong queue" on average. In the W system, there is only one queue, so it is automatically the right one.&lt;/LI&gt;
&lt;LI&gt;The W system is &lt;STRONG&gt;fair&lt;/STRONG&gt;; the customer who has waited the longest is always served next. The M system is not fair; customers who happen to be in a "fast" queue can get served before customers in a "slow" queue where they are&amp;nbsp;waiting behind a complex ongoing transaction (or worse, behind a customer who has reached the front of the queue before deciding what to order.)&lt;/LI&gt;
&lt;LI&gt;The W system has in theory&amp;nbsp;higher possible&amp;nbsp;&lt;STRONG&gt;throughput&lt;/STRONG&gt;; the only time that a customer with a fast transaction pending at the&amp;nbsp;front of the queue&amp;nbsp;has to wait a long time is in the unlikely situation that &lt;EM&gt;every&lt;/EM&gt; cashier happens to be busy with a complex transaction. If &lt;EM&gt;any&lt;/EM&gt; cashier is at present serving fast transactions, then they're clearing the front of the queue quickly. In the M system, many fast transactions can get delayed by a &lt;EM&gt;single&lt;/EM&gt; slow one. This drives average throughput way down.&lt;/LI&gt;
&lt;LI&gt;The W system is far more &lt;STRONG&gt;flexible&lt;/STRONG&gt;. New cashiers can be added dynamically when the queue gets too long and removed to perform less pressing tasks when it shortens. In the M system, when a new cashier comes online there can be a disorganized rush to form a new queue; customers are again asked to make a decision about whether to try the new queue or stay in the old one, and this produces new opportunities for perceived&amp;nbsp;unfairness. But worse, when a cashier is &lt;EM&gt;removed&lt;/EM&gt; in the M system, what happens to their queue?&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;It is the importance of this last question that was driven home to me on day one of my vacation. Let's call the airline "D". The D Air Lines baggage check in Seattle-Tacoma International Airport operates on the "M" queueing model. You check yourself in at one of the kiosks, print out your boarding passes, pay your fifteen dollars to check a bag, and then choose one of ten or so queues. &lt;/P&gt;
&lt;P&gt;Now, D has at least &lt;EM&gt;in theory&lt;/EM&gt; eliminated one problem with the M model; the last thing the checkin system tells you is which queue to stand in. I do not know how that system decides which queue is best, or whether any customer actually reads the message. It's a subtle thing; I personally was not expecting the system to give me this information, so I could see how someone could miss it entirely and just pick any old queue.&lt;/P&gt;
&lt;P&gt;But anyways, we're standing in our assigned queue and its moving along. We don't particularly care how long its taking because our flight has been delayed, allegedly by &lt;EM&gt;one&lt;/EM&gt; hour, due to an "unexpected maintenance issue". So we have plenty of time. (As it turns out, we in reality have over &lt;EM&gt;three&lt;/EM&gt; hours of extra time. Which is fine. Airline mechanics reading this: please take all the time you need to ensure that the wings stay on.) And as we're waiting, I point out something&amp;nbsp;to my wife: &lt;EM&gt;the conveyor belt that the luggage is supposed to be going on is not moving&lt;/EM&gt;. Or rather, it's moving by about one bag width per minute -- jerkily starting up and then halting a second later. Luggage is of course arriving at the front of the queues far, far faster than that, so quite an accumulation of luggage is forming. Most of the "servers" aren't having to walk around, but a few people are walking behind the counter, and it's quite comical to see them trying to manoevre around the increasingly tall stacks of luggage piled beside the now-saturated conveyor.&lt;/P&gt;
&lt;P&gt;Leah informs me that her friend C used to be a baggage handler for D Air Lines, but was recently laid off. "Oh, is the recession and slowdown in air travel making for fewer working hours available?" I ask. No, apparently C claims that there was &lt;EM&gt;plenty&lt;/EM&gt; of work for them to do, but the precarious financial state of the airlines has led them to lay off staff and overwork the remaining handlers.&lt;/P&gt;
&lt;P&gt;So anyway, we get to the front of the queue and I look up, trying to make eye contact with the D Air Lines representative directly in front of me. She is fixedly looking at the floor and says loudly&amp;nbsp;in the vicinity of&amp;nbsp;the representative serving the queue beside her "I have to leave now."&amp;nbsp; Which she does, continuing to look fixedly at the floor as she walks away. The other representatives, all busy serving other customers, do not react to this news. They may not have heard it. Or, they might have interpreted it as mere &lt;EM&gt;information&lt;/EM&gt; -- as it was stated -- rather than as a &lt;EM&gt;request&lt;/EM&gt; for someone to deal with the now-abandoned queue.&lt;/P&gt;
&lt;P&gt;I have plenty of time to wait. So I do. I continue to try to make eye contact with the representatives serving the queues on either side of me, but they are either looking at the person they're serving, or looking with dismay at the growing towers of baggage now thoroughly engulfing the unmoving conveyor belt. I wonder whether it is difficult to miss someone less than two metres away staring at you with an expectant smile for minutes on end. Perhaps D Air Lines trains its staff to be good at that, I muse.&lt;/P&gt;
&lt;P&gt;The minutes continue to pass. The queue behind me continues to grow. I reflect upon how this is a failure not just of customer service, but of application of basic results in queueing theory that you'd think an airline would be good at. I realize that I have a blog article in here somewhere, which makes me happy. I realize that I am now thinking about work while I'm on vacation, which makes me vexed. So on balance, it's a wash.&lt;/P&gt;
&lt;P&gt;About five minutes into this, the traveller behind me politely taps me on the shoulder and asks "You're going to Michigan too, right? Am I in the right line?"&lt;/P&gt;
&lt;P&gt;I turn to half face her, and half face the D Air Lines employee who has been so successfully ignoring me and the couple dozen people behind me. "&lt;EM&gt;Ma'am&lt;/EM&gt;," I say, "&lt;EM&gt;look at the larger picture. You are standing in a queue that is being served by no one to put your bags on a conveyor belt that is not moving. Most of the baggage handlers who would be taking your bags off the belt have been fired, and even if there were baggage handlers, the aircraft cannot fly, and probably is not even at this airport. Clearly&amp;nbsp;you and I have not chosen the wrong &lt;STRONG&gt;queue&lt;/STRONG&gt;; we have chosen the wrong &lt;STRONG&gt;airline&lt;/STRONG&gt;&lt;/EM&gt;."&lt;/P&gt;
&lt;P&gt;Amazingly enough, that aforementioned representative &lt;EM&gt;immediately&lt;/EM&gt; starts servicing our queue.&lt;/P&gt;
&lt;P&gt;Though no&amp;nbsp;indication whatsoever that there had been any sort of problem in the&amp;nbsp;first place was forthcoming, to his credit he was polite, seemed reasonably competent at taking my bag, and added my bag to the tower. As we walked away I looked back and saw the fellow now servicing both queues; those queues were now each running at half speed, which I suppose is better than nothing, though I imagine that the people who had chosen either queue were less than thrilled. The airplane did eventually arrive and we did eventually retrieve the bag, so it all worked out in the end.&lt;/P&gt;
&lt;P&gt;Now you know why most airlines use the "serpentine" W model rather than the M model. It prevents some of these sorts of problems from happening in the first place.&lt;/P&gt;
&lt;P&gt;Things improved considerably after that. A sampling of stuff I saw on my vacation:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Jupiter in opposition&lt;/LI&gt;
&lt;LI&gt;Ganymede&lt;/LI&gt;
&lt;LI&gt;the Perseid meteor shower&lt;/LI&gt;
&lt;LI&gt;trout&lt;/LI&gt;
&lt;LI&gt;frogs&lt;/LI&gt;
&lt;LI&gt;toads&lt;/LI&gt;
&lt;LI&gt;tadpoles&lt;/LI&gt;
&lt;LI&gt;turtles&lt;/LI&gt;
&lt;LI&gt;turkey vultures&lt;/LI&gt;
&lt;LI&gt;swallows&lt;/LI&gt;
&lt;LI&gt;hummingbirds&lt;/LI&gt;
&lt;LI&gt;mergansers&lt;/LI&gt;
&lt;LI&gt;loons&lt;/LI&gt;
&lt;LI&gt;great blue herons&lt;/LI&gt;
&lt;LI&gt;some kind of predatory bird that was against the sun so I couldn't see it clearly but I suspect it was an osprey&lt;/LI&gt;
&lt;LI&gt;chipmunks&lt;/LI&gt;
&lt;LI&gt;bunny rabbits&lt;/LI&gt;
&lt;LI&gt;silver birch&lt;/LI&gt;
&lt;LI&gt;snapdragons&lt;/LI&gt;
&lt;LI&gt;bright green dragonflies&lt;/LI&gt;
&lt;LI&gt;sheep&lt;/LI&gt;
&lt;LI&gt;roosters&lt;/LI&gt;
&lt;LI&gt;fossilized clams&lt;/LI&gt;
&lt;LI&gt;suspiciously damaged wooden kayak paddles: that is,&amp;nbsp;evidence of beaver-shark activity. But how did they get into my kayak storage over the winter? &lt;EM&gt;Are beaver-sharks amphibious?&lt;/EM&gt;&lt;/LI&gt;
&lt;LI&gt;my family&lt;/LI&gt;
&lt;LI&gt;old friends&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;The flight home -- where the baggage check&amp;nbsp;was based on the W model -- was uneventful. &lt;/P&gt;
&lt;P&gt;And so, back to more fabulous adventures in coding. I hope you enjoyed the pre-canned posted I prepared before my vacation.&lt;/P&gt;
&lt;P&gt;Coming up next: one more addendum about iterator blocks.&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9872541" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/vacation/default.aspx">vacation</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/queueing+theory/default.aspx">queueing theory</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/customer+service/default.aspx">customer service</category></item><item><title>Making it easier</title><link>http://blogs.msdn.com/ericlippert/archive/2009/06/15/making-it-easier.aspx</link><pubDate>Mon, 15 Jun 2009 19:56:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9691851</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>19</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/9691851.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=9691851</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;I read an article in a technology column on MSNBC a while back, the upshot of which was “I have umpteen-dozen passwords I’ve got to have memorized these days; I thought technology was supposed to make my life easier!” &lt;/P&gt;
&lt;P&gt;Really?&lt;/P&gt;
&lt;P&gt;First of all, let’s leave aside the obvious fact that our column writer has a technology-driven standard of living which includes affordable access to varieties of &lt;A href="http://www.youtube.com/watch?v=hSELOCMmw4A" mce_href="http://www.youtube.com/watch?v=hSELOCMmw4A"&gt;food, drink, shelter, clothing, medicine, travel, and entertainment&lt;/A&gt; all of which were beyond the wildest dreams of the crowned heads of Europe less than two centuries ago, much less the peasants. Technology does seem to have made lives a lot easier all around. But I rather want to address the actual claim: is the purpose of technology to make your &lt;EM&gt;life&lt;/EM&gt; easier?&lt;/P&gt;
&lt;P&gt;I don’t think it is. I think the purpose of technology is to make &lt;EM&gt;specific tasks&lt;/EM&gt; easier.&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/ericlippert/WindowsLiveWriter/Makingiteasier_8BFF/Surprise_4.jpg" mce_href="http://blogs.msdn.com/blogfiles/ericlippert/WindowsLiveWriter/Makingiteasier_8BFF/Surprise_4.jpg"&gt;&lt;IMG title=Surprise! style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; DISPLAY: inline; MARGIN: 0px 10px 0px 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=244 alt=Surprise! src="http://blogs.msdn.com/blogfiles/ericlippert/WindowsLiveWriter/Makingiteasier_8BFF/Surprise_thumb_1.jpg" width=165 align=left border=0 mce_src="http://blogs.msdn.com/blogfiles/ericlippert/WindowsLiveWriter/Makingiteasier_8BFF/Surprise_thumb_1.jpg"&gt;&lt;/A&gt; Let’s take a task like “communicate with a person in Australia”. Suppose you live in, say, London. In the early 1800’s, the best way to get a message to a colleague in Australia was to write the message out with your goose quill pen, making multiple copies. Wrap each up in oilcloth, the most waterproof substance of the time. Find several convenient Royal Navy ships or merchant ships going to Australia by different routes and at different times; Napoleon might sink several of them but one of them will probably get through. Entrust your waterproofed messages to the captains of those ships, and then wait the several months it takes to get the message around either the Cape or the Horn and the reply back to you.&lt;/P&gt;
&lt;P&gt;I take this opportunity to point out that this already requires an extremely high level of technology. Safely navigating a vessel from England to Australia is not an easy task under the best of circumstances. But obviously we can do better now; the 1800’s solution strikes me as somewhat more arduous and time-consuming as a whole than having to remember your email password.&lt;/P&gt;
&lt;P&gt;The task of communicating with Australia is now much easier, thanks to improved technology. And because we have increased the scope of what is feasible, we can take advantage of new capabilities; in the 1800’s, real-time arbitraging of price differences between Australian and European commodity market derivatives was not anyone’s job description, but I’m sure that there are people pursuing this highly complex task today. The technology enables us to find new ways to complicate our lives, ways that were never possible before because we were too busy spending effort on solving other problems.&lt;/P&gt;
&lt;P&gt;I think about this kind of thing a lot in the context of programming language design. &lt;/P&gt;
&lt;P&gt;When we add a new feature to the language, we almost always make a specific task easier. But in doing so, we also almost always make the language as a whole more complex and therefore harder to learn. We make it more likely that there will be a communications divide between those who know how the new feature works and those who do not, making it harder for everyone to read and understand each other’s code. &lt;/P&gt;
&lt;P&gt;This is one of the major reasons why new features start with “-100 points”, as I’ve often said. Coming up with features that make specific tasks easier is, well, easy. But that’s not enough; the feature has to make things so much better that it justifies the additional complexity added to the language.&lt;/P&gt;
&lt;P&gt;******&lt;/P&gt;
&lt;P&gt;Wikimedia Commons photo by Ted “Rufus” Ross.&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9691851" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Language+Design/default.aspx">Language Design</category></item><item><title>Bug Psychology</title><link>http://blogs.msdn.com/ericlippert/archive/2009/06/01/bug-psychology.aspx</link><pubDate>Mon, 01 Jun 2009 16:48:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9659959</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>28</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/9659959.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=9659959</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;Fixing bugs is hard.&lt;/P&gt;
&lt;P&gt;For the purposes of this posting, I’m talking about those really “crisp” bugs -- those flaws which are entirely due to a failure on the developer’s part to correctly implement some mechanistic calculation or ensure some postcondition is met. I’m not talking about oops, &lt;EM&gt;we just found out that the product name sounds like a rude word in Urdu&lt;/EM&gt;, or &lt;EM&gt;the specification wasn’t quite right so we changed it&lt;/EM&gt; or the code wasn’t &lt;EM&gt;adequately robust in the face of a buggy caller&lt;/EM&gt;. I mean those bugs where you were asked to compute some value and you just plain get the result &lt;EM&gt;wrong&lt;/EM&gt; for some valid inputs.&lt;/P&gt;
&lt;P&gt;Let me give you an example.&lt;/P&gt;
&lt;P&gt;The first bug I ever fixed at Microsoft as a full-time employee was one of those. To understand the context of the bug, &lt;A href="http://blogs.msdn.com/ericlippert/archive/2003/09/16/53013.aspx" mce_href="http://blogs.msdn.com/ericlippert/archive/2003/09/16/53013.aspx"&gt;start by reading this post from the early days of FAIC, and then come back.&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Welcome back, I hope you enjoyed that little trip down memory lane as much as I did.&lt;/P&gt;
&lt;P&gt;Now that you understand how a VT_DATE is stored, that explains this bizarre behaviour in VBScript:&lt;/P&gt;&lt;SPAN class=code&gt;
&lt;P&gt;print DateDiff("h", #12/31/1899 18:00#, #12/30/1899 6:00#) / 24&lt;BR&gt;print DateDiff("h", #12/31/1899 18:00#, #12/29/1899 6:00#) / 24&lt;/P&gt;&lt;/SPAN&gt;
&lt;P&gt;This prints –1.5 and –2.5, as you’d expect. There’s a day and a half between 6 AM December 30th and 6 PM December 31st, and two and a half days between the other two dates. This is perfectly understandable. But if you just subtract the dates:&lt;/P&gt;&lt;SPAN class=code&gt;
&lt;P&gt;print #12/31/1899 18:00# - #12/30/1899 6:00#&lt;BR&gt;print #12/31/1899 18:00# - #12/29/1899 6:00# &lt;/SPAN&gt;
&lt;P&gt;You get 1.5 and 3, not 1.5 and 2.5. Because of the bizarre date format that VT_DATE chooses, when you convert dates to numbers, you cannot safely subtract them if they straddle the magic zero date. That’s why you need the helpful “DateDiff”, “DateAdd” and so on, methods. &lt;/P&gt;
&lt;P&gt;The bug I was assigned was that testing had discovered a particular pair of dates which DateDiff was not subtracting correctly. I took a look at the source code for one of the helper methods that DateDiff used to do one of the computations it needed along the way. To my fresh-out-of-college eyes it looked something like this:&lt;/P&gt;&lt;SPAN class=code&gt;
&lt;P&gt;if (frob(x) &amp;gt; 0 &amp;amp;&amp;amp; blarg(y)) return x – y;&lt;BR&gt;else if (frob(x) &amp;lt; blarg(y) &amp;amp;&amp;amp; blah_blah(x) &amp;gt; 0 || blah_de_blah_blah_blah(x,y)) return frob(x) – x + y + 1;&lt;BR&gt;else if…&lt;/P&gt;&lt;/SPAN&gt;
&lt;P&gt;There were seven such cases.&lt;/P&gt;
&lt;P&gt;My urge was to dive right in and add an eighth special case that fixed the bug. But my ability to get it right in the face of all this complexity concerned me. It seemed like this was an awfully complicated function already for what it was trying to do. &lt;/P&gt;
&lt;P&gt;I researched the history of the code a bit and discovered that in fact variations on this bug had been entered… seven times. Each special case in the code corresponded to a particular bug that had been “fixed”, a term I use guardedly in this case. A great many of those “fixes” had actually introduced new bugs, regressing existing correct behaviour, which then in turn were “fixed” by adding special cases on top of the broken special cases that had been added to “fix” previous bugs.&lt;/P&gt;
&lt;P&gt;I decided that this coding horror would end here. I deleted all the code (all seven lines of it! I was bold!) and started over.&lt;/P&gt;
&lt;P&gt;Deep breath. &lt;/P&gt;
&lt;P&gt;Spec the code requirements first. Then design the code to meet the spec.&amp;nbsp;Then write the code to the design.&lt;/P&gt;
&lt;P&gt;Spec:&lt;/P&gt;
&lt;P&gt;* Input: two doubles representing dates in VT_DATE format.&lt;BR&gt;* VT_DATE format: signed integer portion of double is number of days since 12/30/1899, unsigned fractional part is portion of day gone by.&lt;BR&gt;* For example: –1.75 = 12/29/1899, 6 PM.&lt;BR&gt;* Output: double containing number of days, possibly fractional, between two dates.&amp;nbsp; Differences due to daylight savings time, and so on, to be ignored.&lt;BR&gt;&lt;BR&gt;Design strategy:&lt;/P&gt;
&lt;P&gt;* Problem: Some doubles cannot simply be subtracted because negative dates are not absolute offsets from epoch time&lt;BR&gt;* Therefore, convert all dates to a more sensible date format which can be simply subtracted.&lt;/P&gt;
&lt;P&gt;Code:&lt;/P&gt;&lt;SPAN class=code&gt;
&lt;P&gt;double DateDiffHelper(double vtdate1, double vtdate2)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; return SensibleDate(vtdate2) – SensibleDate(vtdate1);&lt;BR&gt;}&lt;BR&gt;double SensibleDate(double vtdate)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; // negative dates like –2.75 mean “go back two days, then forward .75 days”:&lt;BR&gt;&amp;nbsp; // Transform that into –1.25, meaning “go back 1.25 days”.&lt;BR&gt;&amp;nbsp; return DatePart(vtdate) + TimePart(vtdate);&lt;BR&gt;}&lt;/P&gt;&lt;/SPAN&gt;
&lt;P&gt;I already had helper methods DatePart and TimePart, so I was done. The new code was shorter, far more readable, generated smaller, faster machine code and most important, was &lt;STRONG&gt;clearly correct&lt;/STRONG&gt;. No special cases; no bugs. &lt;/P&gt;
&lt;P&gt;It’s not that my coworkers were dummies. Far from it. These were smart people. But computer geek psychology is such that it is very easy to narrow-focus on the immediately wrong thing, and try to tweak it until it does the right thing. &lt;/P&gt;
&lt;P&gt;When faced with these sorts of “crisp” bugs, I try to restrain myself from diving right in. Rather, I try to psychoanalyze the person – who is, of course, usually my past self – who caused the bug. I ask myself “how was the person who wrote the buggy code fooled into thinking it was correct?” Did they not have a clear specification of what the method was supposed to do? Was it misleading? Did they have a clear plan for how to proceed? If so, where did it go wrong? &lt;/P&gt;
&lt;P&gt;If there never was either a spec or a plan, then for all you know the whole thing might only be working by sheer accident. There could be any number of design flaws in the thing that just haven’t come to light yet. Editing such a beast means adding unknown to unknown. which seldom leads to good results. Sometimes coming up with a new spec, a new plan and scrapping an existing bug farm is the best way to proceed.&lt;/P&gt;
&lt;P&gt;For many years after that, I would ask how to implement DateDiffHelper as my technical question for fresh-out-of-college candidates that I was interviewing for the scripting dev team. I reasoned that if that was the sort of problem I was given on my first day in the office, then maybe that would be a reasonable question to ask a candidate. &lt;/P&gt;
&lt;P&gt;When you ask the same question over and over again, you really get to see the massive difference in aptitude between candidates. I had some candidates who just picked up a marker, wrote a solution straight out on the board, wrote down the test cases they’d use to verify it, mentally ran a few of the tests in their head, and then we’d have another half hour to chat about the weather. And I had some candidates who tried earnestly to write the version using special cases, despite my specifically telling them “you might consider transforming this bad format into something more pleasant to work with”, after they got stuck on the third special case. I’d point out a bug and immediately they’d write down code for another special case, rather than stopping to think about the fact that they’d just written buggy code three times already and told me it was correct three times.&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9659959" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/VBScript/default.aspx">VBScript</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Scripting/default.aspx">Scripting</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Interviewing/default.aspx">Interviewing</category></item><item><title>Arrays considered somewhat harmful</title><link>http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-considered-somewhat-harmful.aspx</link><pubDate>Mon, 22 Sep 2008 21:04:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8961437</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>58</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/8961437.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=8961437</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;I got a moral question from an author of programming language textbooks the other day requesting my opinions on whether or not beginner programmers should be taught how to use arrays.&lt;/P&gt;
&lt;P&gt;Rather than actually answer that question, I gave him a long&amp;nbsp;list of my opinions about&amp;nbsp;arrays, how I use arrays, how&amp;nbsp;we expect arrays to be used in the future, and so on. This gets a bit long, but like Pascal, I didn't have time to make it shorter.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Let me start by saying when you definitely should not use arrays, and then wax more philosophical about the future of modern programming and the role of the array in the coming world.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;You probably should not return an array as the value of a public method or property&lt;/STRONG&gt;, particularly when the information content of the array is logically immutable. Let me give you an example of where we got that horridly wrong in a very visible way in the framework.&amp;nbsp; If you take a look at the documentation for System.Type, you'll find that&amp;nbsp;just looking at the method descriptions gives one a sense of existential dread. One sees a whole lot of sentences like &lt;EM&gt;"Returns an array of Type objects that represent the constraints on the current generic type parameter." &lt;/EM&gt;Almost every method on System.Type returns an array it seems.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Now think about how that must be implemented. When you call, say, GetConstructors() on typeof(string), the implementation cannot possibly do this, as sensible as it seems.&lt;/P&gt;&lt;SPAN class=code&gt;
&lt;P&gt;public class Type { &lt;BR&gt;&amp;nbsp;&amp;nbsp; private ConstructorInfo[] ctorInfos;&lt;BR&gt;&amp;nbsp;&amp;nbsp; public ConstructorInfo[] GetConstructors()&lt;BR&gt;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (ctorInfos == null) ctorInfos = GoGetConstructorInfosFromMetadata();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return ctorInfos;&lt;BR&gt;&amp;nbsp;&amp;nbsp; }&lt;/P&gt;&lt;/SPAN&gt;
&lt;P&gt;Why? Because now the caller can take that array and &lt;EM&gt;replace the contents of it with whatever they please&lt;/EM&gt;. Returning an array means that &lt;STRONG&gt;you have to make a fresh copy of the array every time you return it&lt;/STRONG&gt;. You get called a hundred times, you’d better make a hundred array instances, no matter how large they are. It’s a performance nightmare – particularly if, like me, you are considering&amp;nbsp;using reflection to &lt;EM&gt;build a compiler&lt;/EM&gt;. Do you have any idea how many times a second I try to get type information out of reflection?&amp;nbsp; Not nearly as many times as I could; every time I do it’s another freakin’ array allocation!&lt;/P&gt;
&lt;P&gt;The frameworks designers were not foolish people; unfortunately, we did not have generic types in .NET 1.0. clearly the sensible thing now for GetConstructors() to return is IList&amp;lt;ConstructorInfo&amp;gt;. You can build yourself a nice read-only collection object once, and then just pass out references to it as much as you want. &lt;/P&gt;
&lt;P&gt;What is the root cause of this malaise? It is simple to state: The caller is requesting &lt;EM&gt;values&lt;/EM&gt;.&amp;nbsp; The callee fulfills the request by handing back &lt;EM&gt;variables&lt;/EM&gt;. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;An array is a collection of variables.&lt;/STRONG&gt; The caller doesn’t &lt;EM&gt;want&lt;/EM&gt; variables, but it’ll take them if that’s the only way to get the values. But in this case, as in most cases, &lt;EM&gt;neither the callee nor the caller wants those variables to ever vary&lt;/EM&gt;. Why on earth is the callee passing back variables then? &lt;STRONG&gt;Variables vary&lt;/STRONG&gt;. Therefore, a fresh, different variable must be passed back every time, so that if it does vary, nothing bad happens to anyone else who has requested the same values.&lt;/P&gt;
&lt;P&gt;If you are writing such an API, wrap the array in a ReadOnlyCollection&amp;lt;T&amp;gt; and return an IEnumerable&amp;lt;T&amp;gt; or an IList&amp;lt;T&amp;gt; or something, but not an array.&amp;nbsp; (And of course, do not simply cast the array to IEnumerable&amp;lt;T&amp;gt; and think you’re done!&amp;nbsp; That is still passing out variables; the caller can simply cast back to array!&amp;nbsp; Only pass out an array if it is wrapped up by a read-only object.)&lt;/P&gt;
&lt;P&gt;That’s the situation at present. What are the implications of array characteristics for the future of programming and programming languages?&lt;/P&gt;
&lt;P&gt;&lt;U&gt;Parallelism Problems&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;The physics aspects of Moore’s so-called “Law” are failing, as they eventually must. Clock speeds have stopped increasing, transistor density has stopped increasing. The laws of thermodynamics and the Uncertainty Principle are seeing to that. But manufacturing costs per chip are still falling, which means that our only hope of Moore’s "Law" continuing to hold over the coming decades is to cram more and more processors into each box.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;We’re going to need programming languages that allow mere mortals to write code that is parallelizable to multiple cores.&lt;/P&gt;
&lt;P&gt;Side-effecting change is the enemy of parallelization. Parallelizing in a world with observable side effects means locks, and locks means choosing between implementing lock ordering and dealing with random crashes or deadlocks. Lock ordering requires global knowledge of the program. Programs are becoming increasingly complex, to the point where one person cannot reasonably and confidently have global knowledge. Indeed, we prefer programming languages to have the property that programs in them can be understood by understanding one part at a time, not having to swallow the whole thing in one gulp.&lt;/P&gt;
&lt;P&gt;Therefore we tools providers need to create ways for people to program effectively &lt;EM&gt;without causing observable side effects&lt;/EM&gt;. &lt;/P&gt;
&lt;P&gt;Of all the sort of “basic” types, &lt;STRONG&gt;arrays most strongly work against this goal&lt;/STRONG&gt;. An array’s whole purpose is to be a mass of mutable state. Mutable state is hard for both humans and compilers to reason about. It will be hard for us to write compilers in the future that generate performant multi-core programs if developers use a lot of arrays.&lt;/P&gt;
&lt;P&gt;Now, one might reasonably point out that List&amp;lt;T&amp;gt; is a mass of mutable state too. But at least one could create a threadsafe list class, or an immutable list class, or a list class that has transactional integrity, or&amp;nbsp;uses some form of isolation&amp;nbsp;or whatever. We have an extensibility model for lists because &lt;EM&gt;lists are classes&lt;/EM&gt;. We have no ability to make an “immutable array”. Arrays are what they are and they’re never going to change.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;Conceptual Problems&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;We want C# to be a language in which one can draw a line between code that implements a mechanism and code that implements a policy. &lt;/P&gt;
&lt;P&gt;The “C” programming language is all about mechanisms. It lays bare almost exactly what the processor is actually doing, providing only the thinnest abstraction over the memory model. And though we want you to be able to write programs like that in C#, most of the time people should be writing code in the “policy” realm. That is, code that emphasizes &lt;EM&gt;what the code is supposed to do, &lt;/EM&gt;not &lt;EM&gt;how it does it.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;Coding which is more declarative than imperative, coding which avoids side effects, coding which emphasizes algorithms and purposes over mechanisms, that kind of coding is the future in a world of parallelism. (And you’ll note that LINQ is designed to be declarative, strongly abstract away from mechanisms, and be free of side effects.)&lt;/P&gt;
&lt;P&gt;Arrays work against all of these factors. Arrays demand imperative code, arrays are all about side effects, arrays make you write code which emphasizes how the code works, not what the code is doing or why it is doing it. Arrays make optimizing for things like “swapping two values” easy, but destroy the larger ability to optimize for parallelism.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;Practical Problems&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;And finally, given that arrays are mutable by design, the way an array restricts that mutability is deeply weird. All the &lt;EM&gt;contents&lt;/EM&gt; of the collection are mutable, but the &lt;EM&gt;size&lt;/EM&gt; is fixed.&amp;nbsp; What is up with that? Does that solve a problem anyone actually has?&lt;/P&gt;
&lt;P&gt;For this reason alone I do almost no programming with arrays anymore. Arrays simply do not model any problem that I have at all well – I rarely need a collection which has the rather contradictory properties of being &lt;EM&gt;completely mutable&lt;/EM&gt;, and at the same time, &lt;EM&gt;fixed in size&lt;/EM&gt;. If I want to mutate a collection it is almost always to add something to it or remove something from it, not to change what value an index maps to.&lt;/P&gt;
&lt;P&gt;We have a class or interface for everything I need. If I need a sequence I’ll use IEnumerable&amp;lt;T&amp;gt;, if I need a mapping from contiguous numbers to data I’ll use a List&amp;lt;T&amp;gt;, if I need a mapping across arbitrary data I’ll use a Dictionary&amp;lt;K,V&amp;gt;, if I need a set I’ll use a HashSet&amp;lt;T&amp;gt;. I simply don’t need arrays for anything, so I almost never use them. They don’t solve a problem I have better than the other tools at my disposal.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;Pedagogic Problems&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;It is&amp;nbsp;important that beginning programmers understand arrays; it is an important and widely used concept. But it is also important to me that they understand the weaknesses and shortcomings of arrays. In almost every case, there is a better tool to use than an array.&lt;/P&gt;
&lt;P&gt;The difficulty is, pedagogically, that it is hard to discuss the merits of those tools without already having down concepts like classes, interfaces, generics, asymptotic performance, query expressions, and so on. It’s a hard problem for the writer and for the teacher. Fortunately, for me, it's not a problem that I personally have to solve.&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8961437" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Performance/default.aspx">Performance</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Code+Quality/default.aspx">Code Quality</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Threading/default.aspx">Threading</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Software+development+methodology/default.aspx">Software development methodology</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Immutability/default.aspx">Immutability</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Arrays/default.aspx">Arrays</category></item><item><title>High maintenance</title><link>http://blogs.msdn.com/ericlippert/archive/2008/09/08/high-maintenance.aspx</link><pubDate>Mon, 08 Sep 2008 19:36:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8934196</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>45</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/8934196.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=8934196</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;The other day I went to buy some snack from the snack machine in the kitchen. The snack I wanted was in slot B-10, so I put in my coins, press B - one - zero, hey wait a minute there's no zero button! And why is it serving me up the snack on the left end of the machine instead of the right?&amp;nbsp;Aha, there is a button marked "10", which is the one I was supposed to press. Instead I got snack B1. How irksome!&lt;/P&gt;
&lt;P&gt;And then I laughed at my plight, because of course Steve Maguire told &lt;EM&gt;the same story&lt;/EM&gt; about Microsoft vending machines in &lt;U&gt;Writing Solid Code&lt;/U&gt; &lt;EM&gt;fifteen years ago&lt;/EM&gt;. Maguire went on to make an analogy between bad candy machine interfaces and bad software interfaces; a "candy machine interface" is one which leads the caller to make a plausible but wrong choice.&lt;/P&gt;
&lt;P&gt;Coincidentally, I was asked to review a fragment of code recently that got me thinking again about candy machine interfaces. This isn't &lt;EM&gt;exactly&lt;/EM&gt; the kind of bad interface that Maguire was talking about -- but I'm getting ahead of myself. Let's take a look at the code:&lt;/P&gt;&lt;SPAN class=code&gt;
&lt;P mce_keep="true"&gt;public static class StreamReaderExtensions&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static IEnumerable&amp;lt;string&amp;gt; Lines(this StreamReader reader)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (reader== null)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new ArgumentNullException("reader");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; reader.BaseStream.Seek(0, SeekOrigin.Begin);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string line;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while ((line = reader.ReadLine()) != null)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; yield return line;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/SPAN&gt;
&lt;P&gt;The&amp;nbsp;&lt;EM&gt;idea&lt;/EM&gt; of this code is awesome.&amp;nbsp;I use this technique all the time in my own programs when I need to manipulate a file. Being able to treat a text file as a sequence of lines is very handy. However, the &lt;EM&gt;execution&lt;/EM&gt; of it has some problems.&lt;/P&gt;
&lt;P&gt;The first flaw is the bug I discussed in my earlier post on &lt;A class="" href="http://blogs.msdn.com/ericlippert/archive/2007/09/05/psychic-debugging-part-one.aspx" mce_href="http://blogs.msdn.com/ericlippert/archive/2007/09/05/psychic-debugging-part-one.aspx"&gt;psychic debugging&lt;/A&gt;. Namely, the null check is not performed when the method is called, but rather, when the returned enumerator is moved for the first time. That means that the exception isn't thrown until possibly far, far away from the actual site of the error, which is potentially confusing. &lt;/P&gt;
&lt;P&gt;The second flaw is that the "while" loop condition is a bit hard to read because it tries to do so much in one line. Shorter code is not magically faster than longer code that does the same thing; write the code so that it is maximally readable.&lt;/P&gt;
&lt;P mce_keep="true"&gt;But there's a deeper flaw here. To get at it, first let me state a crucial fact about the relationship between a stream reader and a stream:&lt;/P&gt;
&lt;P&gt;A StreamReader “owns” its underlying stream. That is, once a stream has been handed to a stream reader by a caller, the caller should not muck with the stream ever again. The stream reader will be seeking around in the stream; mucking around with the stream could interfere with the stream reader, and the stream reader will interfere with anyone trying to use the stream. The stream reader emphasizes its ownership by &lt;EM&gt;taking responsibility for disposing the stream when the stream reader is itself disposed.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;Now, “Lines” defines an object, and what does that object do right off the bat?&amp;nbsp; &lt;STRONG&gt;It mucks around with the underlying stream.&lt;/STRONG&gt; This is big red flag #1. This smells &lt;EM&gt;terrible&lt;/EM&gt;. No one should be messing with that stream but the reader.&lt;/P&gt;
&lt;P&gt;Furthermore, think about it from the caller’s point of view. Maybe the caller knows that there are a bunch of bytes that it wants to skip, so it deliberately hands a StreamReader to Lines() which has been positioned somewhere past the beginning of the file. But Lines() thinks that it knows best and &lt;STRONG&gt;ignores the information that the caller has given it&lt;/STRONG&gt;. This is big red flag #2.&lt;/P&gt;
&lt;P&gt;(The reason why the original code was seeking back was because the same reader was being "recycled" many times to read the same file over and over again, which is yet another red flag. Readers are cheap; you can have multiple readers on one file, there's no need to hoard them and reuse them.)&lt;/P&gt;
&lt;P&gt;The third big red flag for me here is the ownership issue. When you hand a stream to a reader, the reader takes care of everything for you from then on – that’s the “contract” between the reader and the caller.&amp;nbsp; “Lines()” does not have that contract. Lines()’s contract says &lt;EM&gt;“attention caller: I am taking ownership of this reader. I am going to muck with its underlying stream. You must never use this reader for anything ever again – except that I’m not going to dispose it for you. If you want the reader and its stream closed then you are going to have to keep a reference to it around until &lt;STRONG&gt;you&lt;/STRONG&gt; can prove that &lt;STRONG&gt;I &lt;/STRONG&gt;am done with it. And if you get it wrong, either I crash or you get a resource leak. So get it right.”&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;This is a &lt;EM&gt;terrible&lt;/EM&gt; contract to impose upon a caller, and of course, the imposition here is in no way represented by the signature of the method. You just have to know that “Lines()” owns the reader for the purposes of all its &lt;EM&gt;functionality&lt;/EM&gt;, but not for its &lt;EM&gt;cleanup&lt;/EM&gt;, which is deeply weird.&lt;/P&gt;
&lt;P&gt;In short, &lt;STRONG&gt;the caller needs to know all the details of what is happening in the method in order to use it correctly&lt;/STRONG&gt;; this is a violation of the whole purpose of creating methods. Methods are supposed to abstract away an operation so that you do not have to know what they are doing.&lt;/P&gt;
&lt;P&gt;A fourth red flag is that &lt;STRONG&gt;the task performed by Lines() does not have a clear meaning in terms of the logic of the program&lt;/STRONG&gt;. In looking at every caller of this method it became clear that the desired semantics&amp;nbsp;were “give me the lines of this &lt;STRONG&gt;text file&lt;/STRONG&gt; one at a time”. But we haven’t written a method that does that. In order to get the lines of a text file from Lines() the caller is required to do all kinds of work to make Lines() happy – open the stream, create a reader, call Lines() but keep the reader around, close the reader after we know that the iterator is done.&lt;/P&gt;
&lt;P&gt;This code makes the caller do a whole bunch of things -- things that have to be done in exactly the right order but potentially distributed out over long stretches of time and disparate code locations. This method is very "high-maintenance"! Everything has to be just right all the time in order for it to be happy and get along with others; anything out of place and things will start going wrong.&lt;/P&gt;
&lt;P&gt;Finally,&amp;nbsp;we have an example of premature generality here -- if the intention is to read a text file, then write a method that reads a text file. If you don't need the power of reading lines from an arbitrary stream, maybe don't implement that. Cut down on your testing burden.&lt;/P&gt;
&lt;P&gt;Some relatively simple changes fix it up:&lt;/P&gt;&lt;SPAN class=code&gt;
&lt;P&gt;public static class FileUtilities&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static IEnumerable&amp;lt;string&amp;gt; Lines(string filename)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (filename == null)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new ArgumentNullException("filename");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return LinesCore(filename);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private static IEnumerable&amp;lt;string&amp;gt; LinesCore(string filename)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.Assert(filename != null);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; using(var reader = new StreamReader(filename))&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; while (true)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;{ &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; string line = reader.ReadLine();&lt;BR&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (line == null) &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;yield break;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;yield return line;&lt;BR&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/SPAN&gt;
&lt;P&gt;And now everything is crystal clear. The purpose of this thing is to read the lines out of a text file. The caller gives it the name of a file, it gives you the lines, and we’re done. The caller does not have to worry about anything else – the iterator takes care of opening the file, cleaning up when its done, and so on. There’s no messing around with streams at all; we have now provided &lt;STRONG&gt;an abstraction over the file system&lt;/STRONG&gt; to the caller.&lt;/P&gt;
&lt;P&gt;Whenever you write a method &lt;STRONG&gt;think about the contract of that method&lt;/STRONG&gt;. What burdens are you imposing upon the caller? Are they reasonable burdens?&amp;nbsp; The purpose of a method should be to make the caller’s life easier; the original version of Lines() makes life harder on the caller. The new version makes life easier. &lt;STRONG&gt;Don't write high-maintenance methods.&lt;/STRONG&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8934196" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Code+Quality/default.aspx">Code Quality</category></item><item><title>Tasty Beverages</title><link>http://blogs.msdn.com/ericlippert/archive/2008/08/19/tasty-beverages.aspx</link><pubDate>Wed, 20 Aug 2008 00:09:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8879813</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>35</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/8879813.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=8879813</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;“&lt;EM&gt;Diet Dr. Pepper tastes more like regular Dr. Pepper.”&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;That was a previous advertising slogan for Diet Dr. Pepper, my personal favourite source of both caffeine and phenylalanine; I’m drinking it right now as I write this. &lt;/P&gt;
&lt;P&gt;The present slogan is the brain-achingly oxymoronic “&lt;EM&gt;Diet Dr. Pepper: There’s Nothing Diet About It&lt;/EM&gt;” – really?&amp;nbsp; Seems like one ought to change the name then, if the name of one’s product is so misleading as to &lt;EM&gt;require its complete and utter disavowal in the slogan&lt;/EM&gt;. &lt;/P&gt;
&lt;P&gt;But that’s not what I want to talk about today. I actually want to talk about &lt;EM&gt;predicates&lt;/EM&gt;. &lt;/P&gt;
&lt;P&gt;The word “predicate” is one of those slippery words that has multiple technical meanings depending on the domain, all related but subtly different enough that one really ought to carefully call out how one is using the term. &lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;In &lt;EM&gt;C# programming&lt;/EM&gt; a predicate is a function which takes a bunch of arguments and returns a Boolean. customer=&amp;gt;customer.Age &amp;lt; 21, for example. &lt;/LI&gt;
&lt;LI&gt;In &lt;EM&gt;mathematical logic&lt;/EM&gt;, a predicate is a function (or relation) which indicates the membership of a set. For example, “all integers x such that x is both even and prime” is a predicate for the set containing only the number 2.&lt;/LI&gt;
&lt;LI&gt;In &lt;EM&gt;English&lt;/EM&gt; &lt;EM&gt;grammar&lt;/EM&gt;, a predicate is that part of a statement which claims to represent a fact about the subject. For example, in “&lt;EM&gt;Thucydides fought in the Peloponnesian War&lt;/EM&gt;”, “&lt;A class="" href="http://blogs.msdn.com/ericlippert/archive/2004/08/18/the-attribute-of-manliness.aspx" mce_href="http://blogs.msdn.com/ericlippert/archive/2004/08/18/the-attribute-of-manliness.aspx"&gt;Thucydides&lt;/A&gt;” is the subject and the rest is the predicate. This predicate happens to be true for the subject “Thucydides”, and false for a different subject, say, “Julius Caesar”.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;What all of these things have in common is that &lt;STRONG&gt;a predicate is something into which you can "substitute" a value to obtain&amp;nbsp;a result&amp;nbsp;of either truth or falsity.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;What on earth does this have to do with Diet Dr. Pepper tasting more like regular Dr. Pepper?&lt;/P&gt;
&lt;P&gt;Is “&lt;EM&gt;tastes more like regular Dr. Pepper&lt;/EM&gt;” actually a predicate? If it is then when it is applied to a subject it must produce a statement which can be classified as true or false. Let’s leave the subjective nature of taste aside for a moment; that’s not the fundamental logical problem here. &lt;/P&gt;
&lt;P&gt;Rather, consider this utterance: “&lt;EM&gt;Diet Dr. Pepper tastes more like&lt;/EM&gt;”&amp;nbsp; Is “&lt;EM&gt;tastes more like&lt;/EM&gt;” a predicate? Of course not. That utterance doesn’t make any sense. Tastes more like… what? This utterance &lt;STRONG&gt;cannot be classified as true or false for any subject&lt;/STRONG&gt;, so the latter part of it cannot actually be a predicate.&lt;/P&gt;
&lt;P&gt;But the same thing goes for “&lt;EM&gt;tastes more like regular Dr. Pepper&lt;/EM&gt;”!&amp;nbsp; Tastes more like regular Dr. Pepper… than what?&lt;/P&gt;
&lt;P&gt;In order to actually be a predicate it needs more objects. For example, “&lt;EM&gt;Diet Dr. Pepper tastes more like regular Dr. Pepper than a pint of Guinness tastes like a mango lassi&lt;/EM&gt;” is a statement which actually has a truth value.&amp;nbsp;Perhaps a subjective and&amp;nbsp;arguable truth value, but at least this sentence has the form of a statement with a subject and a real predicate now. The original slogan’s “predicate” isn’t really a predicate at all; one might think of it as a &lt;STRONG&gt;pseudopredicate&lt;/STRONG&gt;.&lt;/P&gt;
&lt;P&gt;Advertisers &lt;EM&gt;love&lt;/EM&gt; pseudopredicates. Once you realize that they exist you see them all the time. Advertisers love them because they make no testable claim which could be shown to be false in a court of law. Rather, they rely on either the irrational belief that “more” anything means “better”, or upon your brain’s ability to fill in the rest of the objects which they intend you to infer.&lt;/P&gt;
&lt;P&gt;In this particular case, I imagine that the crafters of this slogan intended your brain to fill in “&lt;EM&gt;Diet Dr. Pepper tastes more like regular Dr. Pepper than our previous formulation of Diet Dr. Pepper tasted like regular Dr. Pepper&lt;/EM&gt;” – that is, they want to make the claim that the product has improved without making the admission that the previous formulation was less than delicious. &lt;/P&gt;
&lt;P&gt;Or, perhaps they want you to fill in “&lt;EM&gt;Diet Dr. Pepper tastes more like regular Dr. Pepper than Diet Coke tastes like regular Coke&lt;/EM&gt;” – that is, they want to assert that their product is superior to a competing product. This assertion is in my personal opinion true, but the Coca Cola company could potentially take issue with if stated baldly as an objective claim. By relying upon a pseudopredicate to make a slogan which actually is so malformed as to have no truth value at all, the copywriters duck these thorny issues. There are lots of ways that clever advertisers leverage our tendancies to "fill in the blanks" in order to sell products.&lt;/P&gt;
&lt;P&gt;That’s not actually what I want to talk about today either.&amp;nbsp;&amp;nbsp;I &lt;EM&gt;actually&lt;/EM&gt; want to talk about &lt;EM&gt;writing secure code&lt;/EM&gt;.&lt;/P&gt;
&lt;P&gt;What on earth does Diet Dr. Pepper tasting more like regular Dr. Pepper have to do with writing secure code?&lt;/P&gt;
&lt;P&gt;The other day I got a question about the characteristics of a particular bit of source code obfuscation technology, which we shall call X; what the technology actually consists of and what the precise question was&amp;nbsp;are irrelevant to this discussion. &lt;/P&gt;
&lt;P&gt;I answered the question with a question; I asked why it was that the questioner wanted to use technology X. The answer was “&lt;EM&gt;To protect the source code&lt;/EM&gt;”. Leave aside for the moment the fact that I could probably have deduced from the original query that the questioner was interested in protecting some resource. There’s a deeper problem here. In the utterance “&lt;EM&gt;technology X protects the source code&lt;/EM&gt;”, is “&lt;EM&gt;protects the source code&lt;/EM&gt;” a predicate, or a pseudopredicate?&lt;/P&gt;
&lt;P&gt;It’s a pseudopredicate. There is an object missing. To make this a predicate, it needs to be something like “&lt;EM&gt;protects the source code from casual inspection and editing by snoopy people&lt;/EM&gt;” – as it happens, this predicate was true for technology X. What I was rather worried about was that the questioner actually had in his head the predicate “&lt;EM&gt;protects the database administrator password that I’ve stuck into my source code from discovery and misuse by a determined and intelligent attacker&lt;/EM&gt;”. That predicate happens to be utterly false for technology X. Because he was&amp;nbsp;not actually stating a predicate that&amp;nbsp;could be true or false of X&amp;nbsp;I was unable to answer the guy's question about X.&lt;/P&gt;
&lt;P&gt;I never, ever lock my car doors anymore. Why?&amp;nbsp;I drive a soft-top convertible.&amp;nbsp;One day I woke up to discover that someone had sliced open the top and unlocked the car. The two bucks in quarters I keep in the car for parking meters was a trivial loss compared to the hundreds my insurance company paid to get the top replaced and the hours of my time wasted in dealing with the situation. The locks are not a mitigation to that vulnerability at all! Locking my car doors makes it &lt;EM&gt;more&lt;/EM&gt;&amp;nbsp;prone to be damaged, not less.&lt;/P&gt;
&lt;P&gt;I do, however, lock my house, to protect it against random people wandering in. However, the locks are hardly any mitigation to the vulnerability of the house to determined attack from a wily, hostile burglar. It would be foolish of me to say that “&lt;EM&gt;the locks protect my house&lt;/EM&gt;” without mentioning the &lt;STRONG&gt;threat&lt;/STRONG&gt;. &lt;/P&gt;
&lt;P&gt;What I’m rambling on about here is this: the fitness of a particular security technology to &lt;STRONG&gt;mitigate a vulnerability&lt;/STRONG&gt; can only be evaluated in the context of a &lt;STRONG&gt;stated threat&lt;/STRONG&gt; against a &lt;STRONG&gt;stated resource&lt;/STRONG&gt;. That’s because &lt;STRONG&gt;every security technology is designed to mitigate specific vulnerabilities to particular threats&lt;/STRONG&gt;. When you’re evaluating the benefit of a particular security system, make sure that the predicates you are using to talk about the system are actually predicates, not pseudopredicates; &lt;STRONG&gt;state the threats&lt;/STRONG&gt;.&lt;BR&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8879813" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Security/default.aspx">Security</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category></item><item><title>Customer Service Is Not Rocket Science, Part Two</title><link>http://blogs.msdn.com/ericlippert/archive/2008/06/23/customer-service-is-not-rocket-science-part-two.aspx</link><pubDate>Mon, 23 Jun 2008 18:20:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8642629</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>28</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/8642629.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=8642629</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;I find it irritating, but not surprising, when&lt;A class="" href="http://blogs.msdn.com/ericlippert/archive/2004/04/04/107379.aspx" mce_href="http://blogs.msdn.com/ericlippert/archive/2004/04/04/107379.aspx"&gt; I get absurdly bad customer service&lt;/A&gt; from a business whose business model is based on volume and high margins. But I find it quite&amp;nbsp;surprising, and indeed, &lt;EM&gt;greatly amusing&lt;/EM&gt;, to get absurdly bad customer service from&amp;nbsp;a business whose business model is entirely based on quality of service. &lt;/P&gt;
&lt;P&gt;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.&lt;/P&gt;
&lt;P&gt;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&amp;nbsp;find a lawn guy in my area, who we will refer to as "Lawn Guy". &lt;/P&gt;
&lt;P&gt;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.&lt;/P&gt;
&lt;P&gt;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. &lt;/P&gt;
&lt;P&gt;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.&amp;nbsp;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&amp;nbsp;Likert Scale. I wanted to clearly express to Referral Service &lt;EM&gt;exactly&lt;/EM&gt; what my problem was so that they could deal with it appropriately.&amp;nbsp;Here are the emails, slightly reformatted to make them easier to read in this medium, and with names changed to protect the guilty.&lt;/P&gt;
&lt;P&gt;*****&lt;/P&gt;
&lt;P mce_keep="true"&gt;Good afternoon [Referral Service]&lt;/P&gt;
&lt;P mce_keep="true"&gt;[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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Eric Lippert&lt;/P&gt;
&lt;P mce_keep="true"&gt;*****&lt;/P&gt;&lt;SPAN class=spec&gt;
&lt;P mce_keep="true"&gt;Dear Eric,&lt;/P&gt;
&lt;P mce_keep="true"&gt;Thank you for your email. Ratings &amp;amp; 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&amp;nbsp;[Referral Service]&amp;nbsp;customers. For all requests you place with [Referral Service], you'll be asked to submit Ratings &amp;amp; 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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;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&amp;nbsp;a review]&lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;BR&gt;Best Regards,&lt;/P&gt;
&lt;P mce_keep="true"&gt;Andrew Throatwobbler&lt;BR&gt;[Referral Service]&lt;/P&gt;&lt;/SPAN&gt;***** 
&lt;P mce_keep="true"&gt;Mr. Throatwobbler,&lt;/P&gt;
&lt;P mce_keep="true"&gt;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 &lt;EM&gt;me&lt;/EM&gt; to do work for &lt;EM&gt;your&lt;/EM&gt; 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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Eric&lt;/P&gt;
&lt;P mce_keep="true"&gt;*****&lt;/P&gt;&lt;SPAN class=spec&gt;
&lt;P mce_keep="true"&gt;Dear Eric,&lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Thank you for your time and for using [Referral Service].&lt;BR&gt;&lt;BR&gt;Regards,&lt;BR&gt;Nicole Otterbach&lt;/P&gt;&lt;/SPAN&gt;
&lt;P mce_keep="true"&gt;*****&lt;/P&gt;
&lt;P mce_keep="true"&gt;Ms. Otterbach,&lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;You seem to have completely forgotten that you are in the business of &lt;EM&gt;recommending someone to mow my lawn&lt;/EM&gt;! 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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Eric Lippert&lt;/P&gt;
&lt;P mce_keep="true"&gt;*****&lt;/P&gt;&lt;SPAN class=spec&gt;
&lt;P mce_keep="true"&gt;Eric,&lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Andrew Throatwobbler&lt;/P&gt;&lt;/SPAN&gt;
&lt;P mce_keep="true"&gt;*****&lt;/P&gt;
&lt;P mce_keep="true"&gt;Mr. Throatwobbler,&lt;/P&gt;
&lt;P mce_keep="true"&gt;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 "&lt;EM&gt;when you find yourself in a hole, stop digging&lt;/EM&gt;", 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.)&lt;/P&gt;
&lt;P mce_keep="true"&gt;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:&lt;/P&gt;
&lt;P mce_keep="true"&gt;1) First, and most important, &lt;STRONG&gt;express regret&lt;/STRONG&gt; 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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;2) Second, take this as an opportunity put a good light on the organization by &lt;STRONG&gt;distancing yourself from the situation&lt;/STRONG&gt;. Ms. Otterbach did that, by saying that this is not the level of professionalism you would expect.&lt;/P&gt;
&lt;P mce_keep="true"&gt;3) Third, take steps to &lt;STRONG&gt;make the customer's problem better&lt;/STRONG&gt;. You've certainly not done that. You've concentrated solely on &lt;EM&gt;your&lt;/EM&gt; 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; &lt;EM&gt;I care about getting my lawn mowed&lt;/EM&gt;. And yet you keep on telling me about your rating system.&lt;/P&gt;
&lt;P mce_keep="true"&gt;One out of three is not good, particularly since none of these steps are difficult. &lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;
&lt;P mce_keep="true"&gt;However, one good thing that has come out of this is that I now have an article for my blog.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Cheers,&lt;BR&gt;Eric Lippert&lt;/P&gt;
&lt;P mce_keep="true"&gt;*****&lt;/P&gt;
&lt;P mce_keep="true"&gt;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.&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8642629" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Non-computer/default.aspx">Non-computer</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/customer+service/default.aspx">customer service</category></item><item><title>How to not get a question answered</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/20/how-to-not-get-a-question-answered.aspx</link><pubDate>Wed, 20 Feb 2008 18:28:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7726212</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>18</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/7726212.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=7726212</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P mce_keep="true"&gt;Raymond has had lots of great posts over the years on how to not get a question answered. Some of the ways he points out that help ensure that a question goes unanswered are:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Use a &lt;A href="http://blogs.msdn.com/oldnewthing/archive/2008/01/23/7203582.aspx" mce_href="http://blogs.msdn.com/oldnewthing/archive/2008/01/23/7203582.aspx"&gt;difficult&lt;/A&gt; or &lt;A href="http://blogs.msdn.com/oldnewthing/archive/2007/01/18/1488858.aspx" mce_href="http://blogs.msdn.com/oldnewthing/archive/2007/01/18/1488858.aspx"&gt;meaningless&lt;/A&gt; subject line. 
&lt;LI&gt;&lt;A href="http://blogs.msdn.com/oldnewthing/archive/2007/11/08/5973122.aspx" mce_href="http://blogs.msdn.com/oldnewthing/archive/2007/11/08/5973122.aspx"&gt;Ask a grammatically&amp;nbsp;unclear question&lt;/A&gt;. 
&lt;LI&gt;&lt;A href="http://blogs.msdn.com/oldnewthing/archive/2007/09/12/4872632.aspx" mce_href="http://blogs.msdn.com/oldnewthing/archive/2007/09/12/4872632.aspx"&gt;Forget to actually ask a question&lt;/A&gt; (&lt;A class="" href="http://blogs.msdn.com/oldnewthing/archive/2007/03/15/1883515.aspx" mce_href="http://blogs.msdn.com/oldnewthing/archive/2007/03/15/1883515.aspx"&gt;this&lt;/A&gt; is a personal favourite of mine; back when I was working on scripting I would practically daily get an email consisting of a stack trace and a crash report, with &lt;EM&gt;no indication whatsoever&lt;/EM&gt; of what the sender wanted me to do with it. Investigate it? Commiserate? Give career advice?) 
&lt;LI&gt;&lt;A href="http://blogs.msdn.com/oldnewthing/archive/2007/04/13/2106139.aspx" mce_href="http://blogs.msdn.com/oldnewthing/archive/2007/04/13/2106139.aspx"&gt;Ask an unanswerable question over and over again&lt;/A&gt;.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;I would add&amp;nbsp;that&amp;nbsp;&lt;A href="http://blogs.msdn.com/ericlippert/archive/2003/11/03/53333.aspx" mce_href="http://blogs.msdn.com/ericlippert/archive/2003/11/03/53333.aspx"&gt;phrasing the question&amp;nbsp;in terms of the&amp;nbsp;attempted solution&amp;nbsp;rather than&amp;nbsp;the underlying&amp;nbsp;problem&lt;/A&gt; is a great way to&amp;nbsp;have a question remain unanswered. But I already did that. Today's rant, rather, is:&lt;/P&gt;
&lt;P&gt;A great way to not get your question answered is to &lt;STRONG&gt;treat the person that you're asking to share their time and knowledge like they're not a human being&lt;/STRONG&gt;. &lt;/P&gt;
&lt;P&gt;I understand that this is easy to do.&lt;/P&gt;
&lt;P&gt;I understand that when someone is asking a technical question, it is almost always because they're &lt;EM&gt;deeply frustrated&lt;/EM&gt; by their inability to make some important specific complicated thing work the way they'd expect. &lt;/P&gt;
&lt;P&gt;I understand that software makers are a big source of frustration in a more general sense. We all use imperfect software every day; little frustrations mentally accumulate in a way that little victories do not.&lt;/P&gt;
&lt;P&gt;I understand that programming languages and software in general is often &lt;EM&gt;counterintuitive&lt;/EM&gt;, and that understanding the complex reasoning behind a counterintuitive result is often seemingly of little practical import compared to just getting the problem at hand solved.&lt;/P&gt;
&lt;P&gt;I understand that it is very easy to use an email client as an &lt;EM&gt;impersonal and&amp;nbsp;automatic information resource&lt;/EM&gt; something like a search engine, where you just type in your query, you get your result back, and you move on with your day.&lt;/P&gt;
&lt;P&gt;I understand that there is an inherent and pervasive bias in pure-text communication&amp;nbsp;which makes statements intended to be &lt;EM&gt;good-humoured &lt;/EM&gt;sound &lt;EM&gt;sophomoric&lt;/EM&gt;, makes statements which were intended to be &lt;EM&gt;friendly &lt;/EM&gt;sound &lt;EM&gt;smarmy&lt;/EM&gt;, makes statements which were intended to be &lt;EM&gt;enthusiastic&lt;/EM&gt; sound &lt;EM&gt;brash&lt;/EM&gt;, makes statements intended to be &lt;EM&gt;helpful&lt;/EM&gt; sound&amp;nbsp;&lt;EM&gt;condescending&lt;/EM&gt;, makes statements which were intended to be &lt;EM&gt;precise and accurate&lt;/EM&gt; sound &lt;EM&gt;brusque and pedantic, &lt;/EM&gt;makes statements which were intended to be &lt;EM&gt;positive&lt;/EM&gt; sound &lt;EM&gt;neutral&lt;/EM&gt;, and makes statements which were intended to be &lt;EM&gt;neutral&lt;/EM&gt; seem downright &lt;EM&gt;hostile&lt;/EM&gt;. &lt;/P&gt;
&lt;P&gt;(Boy, do I ever understand that. For over four years I have deliberately tried to pitch the tone of this blog as good-humoured, friendly, enthusiastic, helpful, precise, accurate and positive; &lt;STRONG&gt;I suspect that most of the time I fail badly&amp;nbsp;at most or all&amp;nbsp;of those&lt;/STRONG&gt;. Writing is &lt;EM&gt;hard&lt;/EM&gt;.)&lt;/P&gt;
&lt;P&gt;I understand all that, from both sides; I've certainly been on both the sending and receiving ends of all of the above, many times.&lt;/P&gt;
&lt;P&gt;Which is why I try very hard to be helpful to the &lt;EM&gt;complete strangers&lt;/EM&gt; who send me &lt;EM&gt;overtly rude&lt;/EM&gt; and &lt;EM&gt;frequently profane&lt;/EM&gt; emails capped off with a request for me to to take time out of my day to explain something to them. &lt;/P&gt;
&lt;P&gt;I try to see an upset or confused user as an opportunity to make a good impression; sometimes people do a complete 180 when you help them out and are appreciative and grateful. Sometimes they even send an unsolicited note to your manager, which is always pleasant.&lt;/P&gt;
&lt;P&gt;But more often, I never hear from them again after their question is answered.&lt;/P&gt;
&lt;P&gt;None of this is in their own best interests. It makes the human&amp;nbsp;information sources they rely upon less likely to help them now and in the future.&lt;/P&gt;
&lt;P&gt;So, just a friendly reminder. (A &lt;STRONG&gt;friendly, good-humoured, helpful, enthusiastic, positive&lt;/STRONG&gt; reminder!) The people you are sending email to about your technical problems are &lt;EM&gt;people&lt;/EM&gt;. It would be &lt;STRONG&gt;smart&lt;/STRONG&gt; to briefly introduce yourself, describe your problem without insulting the people who created it, indicate what you've done to attempt to solve it yourself, actually &lt;EM&gt;ask&lt;/EM&gt; for help, and later say thank-you for their time whether they manage to actually help you or not. &lt;/P&gt;
&lt;P&gt;It's smart because doing so is in your own best interests.&lt;/P&gt;
&lt;P&gt;It would also be &lt;STRONG&gt;nice&lt;/STRONG&gt;, but I actually am not particularly concerned about "nice" today. &lt;EM&gt;Nice&lt;/EM&gt; is &lt;EM&gt;nice&lt;/EM&gt; to have I suppose. But not being &lt;EM&gt;smart&lt;/EM&gt; is just&amp;nbsp;&lt;EM&gt;dumb&lt;/EM&gt;.&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7726212" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Best+Of+FAIC/default.aspx">Best Of FAIC</category></item><item><title>Packet Gnomes</title><link>http://blogs.msdn.com/ericlippert/archive/2007/10/11/packet-gnomes.aspx</link><pubDate>Thu, 11 Oct 2007 17:04:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5392587</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>19</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/5392587.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=5392587</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;The other morning I got the following question from a reader:&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; I have created a client-server application in C# using asynchronous socket methods [... blah blah blah, a long description of a scenario in which the socket misses packets under certain conditions]&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;My reply was that I believe networks are run by tiny "packet gnomes" who move the packets around on your behalf. Wireless networks are run by "packet fairies", who can fly. When I have to debug a network problem usually I either leave cookies out for the gnomes, or I call in a friend who has less magical/more scientific beliefs about networks.&lt;/P&gt;
&lt;P&gt;Readers: if you have a question about something I actually know about -- design and implementation of programming language tools, the life and works of J.R.R. Tolkien, sailing small craft, piano repair, and so on -- I'm happy to consider it. But there is really very little point asking me about anything else, since odds are good that I am far less clueful on the subject than you already are.&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5392587" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category></item><item><title>Writing Code Isn't Rocket Science (It's Worse Than That)</title><link>http://blogs.msdn.com/ericlippert/archive/2006/04/10/571098.aspx</link><pubDate>Mon, 10 Apr 2006 17:30:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:571098</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>19</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/571098.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=571098</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;Today, an old joke:&lt;/P&gt;
&lt;P&gt;Q: What do rocket scientists say when they want to describe a portion of their work as easy?&lt;BR&gt;A: "This bit isn't exactly brain surgery."&lt;/P&gt;
&lt;P&gt;I think that pretty much everyone would agree that rocket science and brain surgery are both intellectually demanding pursuits. But it seems to me that there's a fundamental qualitative difference between them.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Rockets are devices constructed by humans for specific purposes. Though there may be considerable systemic interactions between all the parts of a rocket which must work harmoniously together, those interactions are the result of careful design. The rocket as a whole can be decomposed into its original parts, and those parts can be independently tested to see if they meet their design criteria. &lt;/P&gt;
&lt;P&gt;Furthermore, the aim of rocket science is usually to deliver a specific payload to a specific place at a specific time.&amp;nbsp; Though it is no mean feat to get to the moon, we can least for all practical purposes calculate the future position of the moon at any time you care to name to any degree of precision. &lt;/P&gt;
&lt;P&gt;And yes, it's hard, and yes, mistakes are made, sometimes with tragic consequences.&amp;nbsp; But fundamentally, rocket science is about shaping raw matter into precise forms to achieve precise tasks.&lt;/P&gt;
&lt;P&gt;Brain surgery isn't nearly so much like that.&amp;nbsp; We're presented with a lump of thinking, dreaming meat that we barely understand how it works and is presently being used to solve all manner of problems that evolution did not design it for. Our understanding of brains is strongest at the very low level – the neurochemical level – and at the very high level – the gross divisions of the brain into the centers that control particular muscles, responses, etc. But we have practically no understanding whatsoever of any level between those – we are no where close to understanding what algorithms the brain uses to recognize faces or write music.&amp;nbsp; And a working, living brain has trillions of interacting parts which were not designed with orthogonality or functional decomposition in mind.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;I've been thinking about the difference between brain surgery and rocket science lately in the context of my coworker &lt;A HREF="/peterhal/archive/2006/01/04/509302.aspx"&gt;Peter Hallam's essay on the difference between writing new code and&amp;nbsp;modifying old code.&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Naively, one would think that adding new features to code would be like rocket science.&amp;nbsp; You understand all the parts, you figure out how to redesign the parts to admit the new desired behaviour, you implement it, test it, and you're done.&amp;nbsp; It's an intellectual challenge, but it's fundamentally amenable to analysis because every part has a clear, well-designed function that can be tested independently.&lt;/P&gt;
&lt;P&gt;But as Peter points out, anyone who has actually tried to add major new functionality to existing code knows that most of the time its more like brain surgery.&amp;nbsp; We know what the code does on a gross level.&amp;nbsp; (Say, tokenize source, parse, create symbol tables, bind type annotations, check reachability, generate code.)&amp;nbsp; We know what any part of it does on the microscopic level. (Say, move the contents of this register to that memory location.)&amp;nbsp; The hard part is understanding what the algorithms are that make up the large-scale behaviour, and understanding how to tweak them to admit the new desired behaviour without breaking anything.&amp;nbsp; (How does tweaking early nullable realization during binding affect rewriting lambdas into expression trees?&amp;nbsp; Who knows?&amp;nbsp; Not me, that's for sure!)&amp;nbsp; Understanding all that stuff is the incredibly hard part; actually putting the code under the knife is comparatively trivial.&lt;/P&gt;
&lt;P&gt;A big part of good implementation of software-in-the-large is making sure that the program is more like a rocket than a brain.&amp;nbsp; Coming up with tools to enable that admirable goal is the hard part.&amp;nbsp; &lt;BR&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=571098" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category></item><item><title>Democracy in Action</title><link>http://blogs.msdn.com/ericlippert/archive/2004/11/03/democracy-in-action.aspx</link><pubDate>Wed, 03 Nov 2004 16:48:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:251753</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/251753.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=251753</wfw:commentRss><description>&lt;font face="Lucida Sans Unicode" color="purple" size="2"&gt; &lt;p&gt;I saw Tom Stoppard's play "Jumpers" recently.&amp;nbsp; The line that got the biggest audience response:&lt;/p&gt; &lt;p&gt;&lt;font color="#000080"&gt;"It's not the &lt;em&gt;voting&lt;/em&gt; that's democracy; it's the &lt;em&gt;counting&lt;/em&gt;."&lt;/font&gt;&lt;/p&gt; &lt;p&gt;True, that. &lt;/p&gt; &lt;p&gt;I've written a bunch more code for my "Google On The Cheap" series but I've been incredibly heads-down between work, planning a wedding, and my latest book project.&amp;nbsp; Expect it "real soon now".&lt;/p&gt;&lt;/font&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=251753" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Non-computer/default.aspx">Non-computer</category></item><item><title>The National Coin Flipping League Championship Series</title><link>http://blogs.msdn.com/ericlippert/archive/2004/10/21/the-national-coin-flipping-league-championship-series.aspx</link><pubDate>Thu, 21 Oct 2004 19:09:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:245872</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>62</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/245872.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=245872</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;No tech today, but a little basic math.&lt;/P&gt;
&lt;P&gt;In baseball, a sport I know little about, apparently the Boston Red Sox have recently come back from a&amp;nbsp;three game&amp;nbsp;deficit to win a best-of-seven series against their traditional rival team, the New York Yankees.&lt;/P&gt;
&lt;P&gt;Baseball is a game which attracts statisticians, and &lt;EM&gt;many&lt;/EM&gt; have noted that this is the first time in major league baseball history that a team has won a best of seven series after being down three games to none. &lt;/P&gt;
&lt;P&gt;However, it has happened twice in hockey.&lt;/P&gt;
&lt;P&gt;I have a modest proposal. Suppose once a year, the National Hockey League and Major League Baseball decide all their various championships without going to all the trouble and expense of playing the game. Rather, they could simply hold a best-of-seven coin-flipping championship. (Call it the Numismatic Hockey League if you'd like.)&lt;/P&gt;
&lt;P&gt;Suppose Boston calls heads. The odds of Boston flipping T T T and then coming back to win with H H H H are one in 128.&lt;/P&gt;
&lt;P&gt;Therefore, there should be one such occurrence on average every 128 series. There are four such series a year: the American and National League finals, one "world" series (for which only North American teams are eligible, strangely enough), and one Stanley Cup. You'd expect to wait 128 / 4 = 32 years on average between occurrences. &lt;/P&gt;
&lt;P&gt;We've been playing pro baseball and hockey, what, about a hundred years in North America? &lt;/P&gt;
&lt;P&gt;Three such series, in about a hundred years -- or, roughly one every 32 years. It seems like the math works out rather nicely. Maybe they &lt;EM&gt;have&lt;/EM&gt; been deciding the games via coin flipping and just not telling anyone. Hmm...&lt;/P&gt;
&lt;P&gt;Is Boston's victory &lt;EM&gt;really&lt;/EM&gt; that impressive? I mean, the last time I played Risk I rolled three sixes on three dice and England crushed Iceland -- odds of that are 1/216, almost twice as long as Boston coming back from a three tail deficit in the National League Coin Flipping Championship. That's because &lt;STRONG&gt;my blue plastic army guys really worked together as a team and gave 110%!&lt;/STRONG&gt; &lt;/P&gt;
&lt;P&gt;And yet it didn't make headlines in even the local paper.&lt;/P&gt;
&lt;P&gt;In related news, if Houston wins their championship, and it ends up being Texas vs. Massachussets in both baseball AND the presidential election, that's going to be freaky weird. What are the odds of that?&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=245872" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rarefied+Heights/default.aspx">Rarefied Heights</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Non-computer/default.aspx">Non-computer</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/baseball/default.aspx">baseball</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/coin+flipping/default.aspx">coin flipping</category></item><item><title>Anthimeria weirds languages</title><link>http://blogs.msdn.com/ericlippert/archive/2004/10/01/anthimeria-weirds-languages.aspx</link><pubDate>Fri, 01 Oct 2004 21:44:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:236740</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>22</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/236740.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=236740</wfw:commentRss><description>&lt;FONT face="Lucida sans unicode"&gt;&lt;FONT color=#800080 size=2&gt;
&lt;P&gt;A little non-technical rant for a Friday.&lt;/P&gt;
&lt;P&gt;Professor Thingo, in a recent blog entry, &lt;/FONT&gt;&lt;A href="http://www.thingo.net/log/show_entry.cgi?index=256" mce_href="http://www.thingo.net/log/show_entry.cgi?index=256"&gt;&lt;U&gt;&lt;FONT color=#0000ff size=2&gt;decries the use of "Gestalt" as a verb&lt;/U&gt;&lt;/FONT&gt;&lt;/A&gt;&lt;FONT color=#800080 size=2&gt; and asks "&lt;I&gt;Does the English language now allow parts of speech to be used entirely interchangeably? Did I miss a memo?&lt;/I&gt;"&lt;/P&gt;
&lt;P&gt;Though I also would personally balk at verbing "Gestalt" and "&lt;/FONT&gt;&lt;A href="http://www.thingo.net/log/show_entry.cgi?index=27" mce_href="http://www.thingo.net/log/show_entry.cgi?index=27"&gt;&lt;U&gt;&lt;FONT color=#0000ff size=2&gt;architecture&lt;/U&gt;&lt;/FONT&gt;&lt;/A&gt;&lt;FONT color=#800080 size=2&gt;" (but not "architect", a perfectly good verb!) I feel compelled to answer the rhetorical question -- &lt;B&gt;yes, by and large English does allow parts of speech to be used interchangeably&lt;/B&gt;. In fact, the very notion of "part of speech" arose from the study of Latin grammar, a language with precious little in common with English. The whole notion of "parts of speech" maps poorly to English, a language which cheerfully uses "&lt;/FONT&gt;&lt;A href="http://pages.prodigy.net/sol.magazine/workshop.htm" mce_href="http://pages.prodigy.net/sol.magazine/workshop.htm"&gt;&lt;U&gt;&lt;FONT color=#0000ff size=2&gt;green&lt;/U&gt;&lt;/FONT&gt;&lt;/A&gt;&lt;FONT color=#800080 size=2&gt;" as an adjective, verb, adverb, noun and interjection.&lt;/P&gt;
&lt;P&gt;Latin, unlike English, is a highly &lt;B&gt;inflected&lt;/B&gt; language. In inflected languages there are roots.&amp;nbsp; You then do things to them that make them into nouns, verbs, plurals, diminutives, whatever you want. The part of speech can usually be determined by the inflection. Some Latin&amp;nbsp;verbs have over a hundred&amp;nbsp;inflections. &lt;/P&gt;
&lt;P&gt;English, by way of contrast, never has more than five verb inflections for a given verb (except the&amp;nbsp;perennial exception,&amp;nbsp;"to be"). Drive-drives-driving-driven-drove, throw a couple nouns for good measure&amp;nbsp;(driver-drivers) and&amp;nbsp;we’re done. Every other form of "drive" is formed by adding more words into the mix.&amp;nbsp; You can figure out whether a noun or a verb is meant from cues such as phrasal verb particles ("back" is ambiguous, "back away" is&amp;nbsp;probably a verb), auxiliary verbs ("turn" is ambiguous, "will turn" is not) and other contextual cues. &lt;/P&gt;
&lt;P&gt;A &lt;STRONG&gt;huge&lt;/STRONG&gt; number of English words are nouns that became verbs without benefit of any kind of inflection or derivation. That's just what English does, and what it's done for centuries, and yet prescriptivists continue to decry it. (Of course, they’ve also done so for decades, so it’s a bit silly for me to decry prescriptivism!)&lt;/P&gt;
&lt;P&gt;I found &lt;/FONT&gt;&lt;A href="http://www.soyouwanna.com/site/syws/wrerrors/wrerrors4.html" mce_href="http://www.soyouwanna.com/site/syws/wrerrors/wrerrors4.html"&gt;&lt;U&gt;&lt;FONT color=#0000ff size=2&gt;this page of bad advice&lt;/U&gt;&lt;/FONT&gt;&lt;/A&gt;&lt;FONT color=#800080 size=2&gt; to be particularly hilarious. This line in particular: &lt;/P&gt;&lt;I&gt;
&lt;P&gt;If you look at a dictionary entry carefully, you'll often see that the word you're looking at was used exclusively as a noun up until 1983 or something like that.&lt;/P&gt;&lt;/I&gt;
&lt;P&gt;Unlike those guys, I actually &lt;I&gt;did&lt;/I&gt; look at a &lt;A href="http://www.oed.com/" mce_href="http://www.oed.com"&gt;dictionary&lt;/A&gt;&amp;nbsp;carefully and discovered that in fact many of the verbings they were decrying had been used as verbs in English for centuries. Two&amp;nbsp;in particular stood out. "Impact", which is &lt;B&gt;actually a verb that became a noun&lt;/B&gt; in the late 18&lt;SUP&gt;th&lt;/SUP&gt; century. (Though, to be fair, the 17&lt;SUP&gt;th&lt;/SUP&gt;-century meaning of "impact" as a verb was more along the sense of "impacted molar" than the physics sense of things colliding – that usage didn’t arise until the 20&lt;SUP&gt;th&lt;/SUP&gt; century.) More ridiculous though is "parent", which they decry as "idiotically new-age" but has been used as a verb in English since at least the mid 1600’s. (Again, to be fair, the &lt;I&gt;intransitive&lt;/I&gt; usage is modern, but the &lt;EM&gt;transitive&lt;/EM&gt; verb sense is very, very old.)&lt;/P&gt;
&lt;P&gt;The&amp;nbsp;earliest known&amp;nbsp;recorded usage of each as a noun and verb is telling.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;&lt;FONT face="Lucida Console" color=#000080&gt;contact: v: 1834 &amp;nbsp;n: 1626&lt;BR&gt;impact:&amp;nbsp; v: 1601 &amp;nbsp;n: 1781&lt;BR&gt;focus:&amp;nbsp;&amp;nbsp; v: 1875 &amp;nbsp;n: 1656&lt;BR&gt;parent:&amp;nbsp; v: 1663 &amp;nbsp;n: 1450&lt;BR&gt;medal:&amp;nbsp;&amp;nbsp; v: 1822 &amp;nbsp;n: 1578&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;"Parent" has been a verb&amp;nbsp;almost as long as "impact" has even been a recorded&amp;nbsp;English word! And anyone who tells you that you shouldn’t use "medal" as a verb because it’s only been used in that sense since 1822 should also be decrying the use of "mail" as a verb (in the postal sense).&amp;nbsp; "Mail"&amp;nbsp;as a verb&amp;nbsp;dates from as recently as 1827.&amp;nbsp;&amp;nbsp;And not to mention "access", which only dates from 1962 as a verb!&lt;/P&gt;
&lt;P&gt;Furthermore, when English introduces new words it frequently takes on both noun and verb forms. Is "Spackle" (a trademark, incidentally) a noun or verb? What about "blog"?&lt;/P&gt;
&lt;P&gt;Look at the over two dozen&amp;nbsp;words I’ve used just in this short essay that are clearly both verbs and nouns –&lt;EM&gt; part, miss, map, green, root, contrast, throw, figure, answer, cue, back, down, turn, will, line, record, sense, mention, date, take, look, tell, form, use,&amp;nbsp;essay …&lt;/EM&gt; &lt;STRONG&gt;Using nouns as verbs is just what English does,&lt;/STRONG&gt; and I think it’s great. Go verb!&lt;/FONT&gt;&lt;FONT face="Times New Roman"&gt;&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=236740" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/English+Usage/default.aspx">English Usage</category></item><item><title>I Have A Mysterious Fifth Sense</title><link>http://blogs.msdn.com/ericlippert/archive/2004/08/20/i-have-a-mysterious-fifth-sense.aspx</link><pubDate>Sat, 21 Aug 2004 02:35:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:218030</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>16</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/218030.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=218030</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;A little fun for a Friday afternoon.&lt;/P&gt;
&lt;P&gt;The economy must be picking up -- I'm getting cold calls from recruiters again for the first time in about four years.&amp;nbsp; Today was the second (and third!) this month.&lt;/P&gt;
&lt;P&gt;However, apparently some of them are just a wee bit disorganized. I just had the following conversations:&lt;/P&gt;
&lt;P&gt;[Ring ring]&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: Hi, this is Eric.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her&lt;/STRONG&gt;: Hi, this is Barbara at XYZ Recruiters. How are you today?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: I am &lt;STRONG&gt;extremely&lt;/STRONG&gt; well! How are you, Barbara?&lt;/P&gt;
&lt;P&gt;[This seemed to &lt;I&gt;completely&lt;/I&gt; flummox Barbara. Perhaps people who are interrupted at work by cold callers do not usually inquire as to her health?]&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her&lt;/STRONG&gt;: Uh. Um. Me? Uh, I'm fine I guess! Thanks for asking!&lt;/P&gt;
&lt;P&gt;[Brief pause -- OK, I guess keeping this conversation going is up to me.]&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: So what's up, Barbara?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her, back to the script&lt;/STRONG&gt;: There's a small company in downtown Seattle that is 60% ex-Microsoft people and they're looking to hire C++ devs. They've just landed a big contract with Foo corp.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: Well thanks for thinking of me Barbara. I'm happy to speak with you but to be fair I first must warn you that I am &lt;STRONG&gt;intensely loyal&lt;/STRONG&gt;.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her&lt;/STRONG&gt;: Oh. Well, thanks for your time. Bye.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: Bye!&lt;/P&gt;
&lt;P&gt;Wow, she didn't put up a fight &lt;I&gt;at all&lt;/I&gt;. Maybe the economy isn't picking up so much. Four years ago recruiters -- who were for some reason invariably female -- would flirt with me and then try to get me interested in crappy database admin jobs in the Cayman Islands, of all places.&lt;/P&gt;
&lt;P&gt;Ah well, back to work. I resolve an old bug that got fixed a while back but never removed from the bug database. I start looking at another bug and researching the history of a particular code change. We've made a minor change to the formatting of an XML file and Mario wants to know whether that was by design or an accident, when...&lt;/P&gt;
&lt;P&gt;[Ring Ring -- hey, the caller id looks familiar...]&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: Hi, this is Eric.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her&lt;/STRONG&gt;: Hi this is...&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: Barbara at XYZ recruiters?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her&lt;/STRONG&gt;: Uh, yes. How...&lt;/P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: What's new? 
&lt;P&gt;[Like Hobbes, I love &lt;A href="http://www.ics.uci.edu/~jmadden/dawning_moment.html" mce_href="http://www.ics.uci.edu/~jmadden/dawning_moment.html"&gt;the moment of dawning comprehension&lt;/A&gt;. Barbara hits it.]&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her&lt;/STRONG&gt;: Wait... did I call you already?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: Yes, about ten minutes ago.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her, trying to place me&lt;/STRONG&gt;: Uh… are you a C++ developer?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: Why yes I am as a matter of fact!&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her, paging in at last&lt;/STRONG&gt;: You're the one who's "intensely loyal", right?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: Indeed. And I still am. Cold-calling recruiting really is kind of about looking for &lt;I&gt;disloyal&lt;/I&gt; people, isn't it? People who will just pick up when something better comes along, right?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Her&lt;/STRONG&gt;: Hey, some people are looking for a change! Wanting change in your life doesn't make you disloyal, does it?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Me&lt;/STRONG&gt;: Well, you're the expert. I'll take your word for it.&lt;/P&gt;
&lt;P&gt;This brought back fond memories of my teenage days. Unlike my crazy friends, I never prank &lt;B&gt;called&lt;/B&gt; people, but I prank &lt;B&gt;answered&lt;/B&gt; them all the time. You phone me, you take that risk. The moral: send email.&lt;/P&gt;
&lt;P&gt;Having two voice lines in the house (one for my 300 baud modem on the Commodore 64, of course) led to ample opportunities for consecutive calls from clueless telemarketers. On the second call I'd just answer with "&lt;EM&gt;Don't say anything! I have a mysterious fifth sense! My psychic powers tell me that your name is Helen, and you want to sell me... magazine subscriptions! Yes?" &lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;Freaked them out every time.&lt;/P&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=218030" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Non-computer/default.aspx">Non-computer</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Dialogue/default.aspx">Dialogue</category></item><item><title>The Attribute Of Manliness</title><link>http://blogs.msdn.com/ericlippert/archive/2004/08/18/the-attribute-of-manliness.aspx</link><pubDate>Wed, 18 Aug 2004 21:38:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:216655</guid><dc:creator>Eric Lippert</dc:creator><slash:comments>17</slash:comments><comments>http://blogs.msdn.com/ericlippert/comments/216655.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ericlippert/commentrss.aspx?PostID=216655</wfw:commentRss><description>&lt;DIV class=mine&gt;
&lt;P&gt;This is a technical, not a political, current-events, linguistic or academic blog. (You know of course that as soon as I say that, it's because I'm about to post something that is political, timely, linguistic and academic. Foreshadowing: your sign of a quality blog!) Despite all that, I was so struck by this passage I read last night that I felt I had to share it. We'll get back to error handling in VBScript or some such topic later this week. &lt;/P&gt;
&lt;P&gt;The writer is discussing semantics, specifically how word meanings and popular opinions change in political debates during wartime. The writer is... well, I'll just let him say it, and talk about the writer afterwards. &lt;/P&gt;
&lt;P&gt;&lt;FONT color=#000080&gt;Words had to change their ordinary meaning: &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#000080&gt;&lt;/FONT&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;FONT color=#000080&gt;reckless audacity came to be considered the courage of a loyal ally ; prudent hesitation, specious cowardice &lt;/FONT&gt;
&lt;LI&gt;&lt;FONT color=#000080&gt;moderation was held to be a cloak for unmanliness, frantic violence became the attribute of manliness&lt;/FONT&gt; 
&lt;LI&gt;&lt;FONT color=#000080&gt;ability to see all sides of a question, inaptness to act on any&lt;/FONT&gt; 
&lt;LI&gt;&lt;FONT color=#000080&gt;cautious plotting, a justifiable means of self-defense &lt;/FONT&gt;
&lt;LI&gt;&lt;FONT color=#000080&gt;the advocate of extreme measures was always trustworthy; his opponent was a man to be suspected &lt;/FONT&gt;
&lt;LI&gt;&lt;FONT color=#000080&gt;the fair proposals of an adversary were met with jealous precautions by the stronger of the two, and not with a generous confidence&lt;/FONT&gt; 
&lt;LI&gt;&lt;FONT color=#000080&gt;revenge also was held of more account than self-preservation &lt;/FONT&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;FONT color=#000080&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#000080&gt;The cause of all these evils was the lust for power arising from greed and ambition; and from these passions proceeded violence.&lt;/FONT&gt; &lt;/P&gt;
&lt;P&gt;Thus &lt;STRONG&gt;Thucydides of Athens, 2435 years ago&lt;/STRONG&gt;. (Translation by Richard Crawley. I've changed the formatting and trimmed it a bit -- Crawley gets a little wordy, but I love the balanced sentences.) &lt;/P&gt;
&lt;P&gt;The first reaction I had upon reading this was "isn't it astonishing how modern Thucydides sounds across the ages? If he'd only thought to coin the snappy term 'doublespeak', he'd have scooped Orwell by a couple millenia!" &lt;/P&gt;
&lt;P&gt;And then I gave my head a shake, because of course I was reasoning backwards. This shouldn't be astonishing in the least; I live in a culture where general opinions on government, politics, warfare, sports and art are more or less just as they were in Classical Greece. It would be more astonishing if Thucydides insights into human nature were not applicable today. &lt;/P&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=216655" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Rants/default.aspx">Rants</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Non-computer/default.aspx">Non-computer</category><category domain="http://blogs.msdn.com/ericlippert/archive/tags/Ancient+Greeks/default.aspx">Ancient Greeks</category></item></channel></rss>