Sunday, March 30, 2014

High-falutin Code #1

"By the time you've got multiple joins, query expressions almost always win" -- Jon Skeet


But.. there are coders who simply love to write more code, perhaps they feel that the more convoluted-looking their code are the smarter they are. Hence most of them even when the code can sometimes use and be made more readable with Linq, will still use lambda instead. They feel they have elite skills just by using lambda.


var itemsOwner = 
    items
        .Join (people, i => i.OwnerId, p => p.PersonId, (i, p) => new { Item = i, Person = p })
        .Join (countries, ip => ip.Person.CountryId, c => c.CountryId, (ip, c) => new { ItemPerson = ip, Country = c })
        .Select (x => new { x.ItemPerson.Item.ItemName,  x.ItemPerson.Person.PersonName, x.Country.CountryName });



Those coders need to be in the movie Crank, let's see how they can write multiple joins in no time, let's see if they can still breath after two joins.


Then the continent name needed be shown on the output:

var itemsOwner = 
    items
        .Join (people, i => i.OwnerId, p => p.PersonId, (i, p) => new { Item = i, Person = p })
        .Join (countries, ip => ip.Person.CountryId, c => c.CountryId, (ip, c) => new { ItemPerson = ip, Country = c })
        .Join (continents, ipc => ipc.Country.CountryId, z => z.ContinentId, (ipc, z) => new { ItemPersonCountry = ipc, Continent = z })
        .Select (x => new { 
            x.ItemPersonCountry.ItemPerson.Item.ItemName, 
            x.ItemPersonCountry.ItemPerson.Person.PersonName, 
            x.ItemPersonCountry.Country.CountryName,
            x.Continent.ContinentName
        });


That's the problem with lambda joins, the aliases of multiple lambda joins cascades to the aliases of the subsequent joins. The aliases become unwieldy and deeply nested.



And there's too many variations of that lambda join, some would prefer the code below, deciding early what properties to project during lambda join. Ironically, though Linq and lambda promotes the use of deferred execution, but here we are, we have a coder deciding upfront that it's better to project user-shown properties right there in the lambda joins.


var itemsOwner = 
    items
        .Join (people, i => i.OwnerId, p => p.PersonId, (i, p) => new { i.ItemName, p.PersonName, p.CountryId })
        .Join (countries, ipc => ipc.CountryId, c => c.CountryId, (ipc, c) => new { ipc.ItemName, ipc.PersonName, c.CountryName });




And then you need to include the brand, oh the DRY headache!

var itemsOwner = 
    items                   
        .Join (people, i => i.OwnerId, p => p.PersonId, (i, p) => new { i.ItemName, p.PersonName, p.CountryId, i.BrandId })
        .Join (brands, ipcb => ipcb.BrandId, b => b.BrandId, (ipcb, b) => new { ipcb.ItemName, ipcb.PersonName, ipcb.CountryId, b.BrandName })
        .Join (countries, ipcb => ipcb.CountryId, c => c.CountryId, (ipcb, c) => new { ipcb.ItemName, ipcb.PersonName, c.CountryName, ipcb.BrandName });


See the problem with lambda joins? Aside from too many variations (deciding upfront to project the user-shown properties during joinings vs deferring that decision on final join or select), there's also the hard problem of giving a proper alias for the subsequent joins.



Then there's a change in the requirement to show the brand name after the item name, you feel the aliases should reflect that fact, so you got to change the alias too:

var itemsOwner = 
    items                   
        .Join (people, i => i.OwnerId, p => p.PersonId, (i, p) => new { i.ItemName, i.BrandId, p.PersonName, p.CountryId })
        .Join (brands, ibpc => ibpc.BrandId, b => b.BrandId, (ibpc, b) => new { ibpc.ItemName, b.BrandName, ibpc.PersonName, ibpc.CountryId })
        .Join (countries, ibpc => ibpc.CountryId, c => c.CountryId, (ibpc, c) => new { ibpc.ItemName, ipcb.BrandName, ibpc.PersonName, c.CountryName });




The highfalutin coder experiences the problems with unwieldy alias names cascading to subsequent joins, nested aliases, and giving good names for aliases, he decided to wise up; he decided he can avoid all the headaches above just by giving a generic (e.g., src) name for all aliases:


var itemsOwner = 
    items                   
        .Join (people, src => src.OwnerId, p => p.PersonId, (src, p) => new { src.ItemName, src.BrandId, p.PersonName, p.CountryId })
        .Join (brands, src => src.BrandId, b => b.BrandId, (src, b) => new { src.ItemName, b.BrandName, src.PersonName, src.CountryId })
        .Join (countries, src => src.CountryId, c => c.CountryId, (src, c) => new { src.ItemName, src.BrandName, src.PersonName, c.CountryName });


By the time all the original developers in the project are replaced, new developers will be left scratching their heads why the old developers didn't bother to give self-describing names for aliases.



Let's say you want to switch the join order (MySQL is notorious on requiring certain order on joins to gain performance) of people and brands..
var itemsOwner = 
    items                   
        .Join (people, src => src.OwnerId, p => p.PersonId, (src, p) => new { src.ItemName, src.BrandId, p.PersonName, p.CountryId })
        .Join (brands, src => src.BrandId, b => b.BrandId, (src, b) => new { src.ItemName, b.BrandName, src.PersonName, src.CountryId })
        .Join (countries, src => src.CountryId, c => c.CountryId, (src, c) => new { src.ItemName, src.BrandName, src.PersonName, c.CountryName });


..you cannot nilly-willy re-order the joins without incurring changes on code, you can not just cut-and-paste the code, this doesn't compile:
var itemsOwner = 
    items                   
        .Join (brands, src => src.BrandId, b => b.BrandId, (src, b) => new { src.ItemName, b.BrandName, src.OwnerId })
        .Join (people, src => src.OwnerId, p => p.PersonId, (src, p) => new { src.ItemName, src.BrandName, p.PersonName, p.CountryId })
        .Join (countries, src => src.CountryId, c => c.CountryId, (src, c) => new { src.ItemName, src.BrandName, src.PersonName, c.CountryName });




Now contrast the humble developer's Linq join code to codes above. The code is very readable:

var itemsOwner = 
    from i in items
    join p in people on i.OwnerId equals p.PersonId
    join b in brands on i.BrandId equals b.BrandId
    join c in countries on p.CountryId equals c.CountryId    
    select new { i.ItemName, b.BrandName, p.PersonName, c.CountryName };


Then switch the join order of people and brands, the changes in Linq-using code is not invasive compared to lambda-using code, you can just cut-and-paste the code, this compiles:

var itemsOwner = 
    from i in items
    join b in brands on i.BrandId equals b.BrandId
    join p in people on i.OwnerId equals p.PersonId    
    join c in countries on p.CountryId equals c.CountryId    
    select new { i.ItemName, b.BrandName, p.PersonName, c.CountryName };



If the coder doesn't see problems with lambda joins and still prefer to write joins using it instead of Linq, he need to star in the movie Crank. I would hazard a guess he would die by the time he is in the third or fourth join; or worse yet was required to insert another join between the existing joins, good luck breathing with that task!


Another lambda join variation, not compounding objects in one alias, giving each joined object their own alias. Drawback is, subsequent joins must repeat the objects from the previous joins, a WET code:
var itemsOwner = 
    items
        .Join (people, src => src.OwnerId, p => p.PersonId, (src, p) => new { Item = src, Person = p })
        .Join (brands, src => src.Item.BrandId, b => b.BrandId, (src, b) => new { src.Item, src.Person, Brand = b })
        .Join (countries, src => src.Person.CountryId, c => c.CountryId, (src, c) => new { src.Item, src.Person, src.Brand, Country = c })
        .Select (x => new { x.Item.ItemName, x.Person.PersonName, x.Brand.BrandName, x.Country.CountryName });


The coder forgot he is using MySQL, it's optimal to join on brands first, then people. As an exercise, try switching the join order of people and brands of the code above, good luck breathing! http://dotnetfiddle.net/3bvQDy



Another variation, this requires investing another class to assist you in your adventure on joining using lambda, this also requires you to violate DRY principle:
var itemsOwner = 
    items
    .Join (people, src => src.OwnerId, p => p.PersonId, (src, p) => new JoinHelper { Item = src, Person = p  } )
    .Join (brands, src => src.Item.BrandId, b => b.BrandId, (src, b) => new JoinHelper { Item = src.Item, Person = src.Person, Brand = b })
    .Join (countries, src => src.Person.CountryId, c => c.CountryId, (src, c) => new JoinHelper { Item = src.Item, Person = src.Person, Brand = src.Brand, Country = c } )
    .Select (x => new { x.Item.ItemName, x.Person.PersonName, x.Brand.BrandName, x.Country.CountryName });



Do you still want to showboat your lambda join skills?



Time to repent your perceived superiority, use Linq to avoid highfalutin lambda code.



The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague -- Edsger Dijkstra


There are cases that lambda is an unnecessary trick, just showboating.

Friday, March 14, 2014

Our Reaction To New Technology

Do you feel technologies like AngularJS, SPA, NoSQL, REST and cloud are against the natural order of things(e.g. ASP.NET, ASP.NET MVC, jQuery, WCF)? There must be a reason why





If you are distraught by revolutionary technologies even you haven't reached 35 yet, what does excite you? Perhaps you aged way too early?


Are you willing to trim down old jobs from your resume?


Perhaps we should have an attitude as young as this CTO: https://twitter.com/b4nn0n/status/347020882511818752



Happy Coding! ツ

Thursday, March 6, 2014

Rethinking C# interface's abstractness and OOP anemicness

Sometimes when we don't (I don't) have a full mastery of the language. dotnetfiddle helps http://dotnetfiddle.net/5mfuRY

So I got to rethink C# interface's members as just abstracts too.. http://www.ienablemuch.com/2012/03/advantage-of-explicit-interface.html ..as the following doesn't work: http://dotnetfiddle.net/7X8QQ8


..or perhaps C# should offer some symmetry as java, C# should allow override keyword on members of implementing class http://dotnetfiddle.net/7X8QQ8


Or perhaps I just don't care about object-orientation anymore and leaning towards more javascript, hence sometimes forgetting fundamental OOP concepts such as that it is allowed for an implementing class to add virtual keyword on implemented interface members. Me trying to forget OOP making me dangerous? lol Don't worry, dotnetfiddle makes me a less dangerous C# developer


And really, sometimes the way we implement OOP (or we think it is OOP) is not really an OOP at all, our OOP is sometimes so anemic


Happy Coding! ツ