Hi Patrick, 

I'll try one last string of arguments why I think that existence guards are the 
right way to go. After that, I'll shut up - because it is obvious that the 
semantics in Linq2Objects-transgressing scenarios is arbitrary, so you can 
define it as you want!

In contrast to (||-4), (||-5) gives you the following (I say simple) semantics:

* Some condition using properties of an object can only be true if the object 
"is there".
* No "projection anomaly".

But, as I and you noted, for Linq2Objects-transgressing expressions we get:

* "Inverse operator anomaly": !(...==...) is not (...!=...)

There are more anomalies, like the &&-commutativity and ||-commutativity, which 
are apparently accepted by all ...

I would be easier to convince if you gave me a somewhat thorough treatment of 
your logic. At least you should demonstrate what happens in (||-4) with 
De'Morgan and associativity - obviously, I'll have to do the same for (||-5) 
(for SQL (triple valued) logic, we all know how to do that; therefore, it 
should also be done for another query logic).

If you really want to convince me (and probably many others), you have to write 
down the *disadvantages* of our proposal. If they are visible - i.e., there are 
examples for them -, then one can see and accept them.

-----

One item which would might me to give in to you:

There is actually *one* implicit assumption which I never questioned - and 
which you apparently do not share: That very simple conditions like

    .Where(a => a.B.C.P == ...)

should use inner joins. I grew up in a world where inner joins were 
significantly cheaper than outer joins ... that's why. If we really use outer 
joins *all the time*, then there is no or-sum-anomaly, because simple 
conditions like 

    .Where(a => a.B.C.P == null)

would return objects where B or B.C is null. (||-4) might be a way to go then 
...

-----

Here are some attempts to reply to your arguments. I'll maybe use quite blunt 
words - please do not take them as attacks, but as a hopefully clear 
formulation of what I consider right or wrong. 

> More and more I believe that just using outer joins is the best
> technique.  Here are the latest reasons:
> 1.  Significantly simpler implementation.

Trat could be the case --> let's try it (although I'm not yet convinced - see 
below after 5.).

> 2.  Same semantics when null references are not present.

That's true for both (||-4) and (||-5) - all the anomalies vanish then! 
Therefore, that's no argument for one over the other.

> 3.  SQL semantics when null references are present (as opposed to
> semantics that are neither SQL nor Object)

I think this is wrong reasoning. Linq (like other, older OQLs) is a 
*navigational query language*. There is no "SQL semantics" in it - never and 
nowhere. For example, in Linq it is true that null == null (and therefore I 
expect that SQL providers do translate the expression a.P == a.Q as

    (...P = ...Q OR ...P IS NULL AND ...Q IS NULL)

- everything else is obviously wrong (under BEHAV-1 ... BEHAV-4); I have not 
tested whether NHib's Linq and EF's provider is correct in this respect). The 
arguments about "SQL semantics" come (a) from some misguided and unfortunate 
sentences in the C# docs when Nullables were introduced; and (b) that 
"implementation thinking" where SQL semantics is allowed to "hi-jack" the Linq 
semantics because "this is 'simpler'". But it is only "simpler" if you toggle 
between Linq and SQL semantics erratically for trivial examples. Instead, one 
should think about whole *classes of queries* (that's how I found the 
projection anomaly) and consider the semantics.
Again: There is no SQL semantics for a.B.C.P == null - simply because there is 
no "navigation a.B.C" in SQL.

> 4.  If existence guards are added to protect against NREs manually, it
> behaves like Linq to Objects.

That's the same for both semantics.

> 5.  Simpler queries, likely leading to performance improvements, and
> at very least being easier to understand.
> 

Well - I should march up my 20 developers, shouldn't I ;-) ? Do you have a 
group of people who have programmed against (||-4) and are satisfied with it = 
did not come to you [or whoever else] and ask about why they find objects they 
are not expected to see? I did not invent that (||-5) semantics as a theorist 
... all that has been used for a few years, and therefore I say that it is 
"simpler", "more efficient" (many inner joins) and "easier to understand".

But I do not see a constructive way how we can settle our different opinions 
what is "simpler" and "easier to understand". Do you???

> I'm actually quite interested in seeing a version of your code that
> just uses the outer joins 

That should be easy ...

> and optimizes to inner joins when possible.

... this is not that easy - but I'll try it out!: In the moment where there is 
a navigation ("a member expression at depth > 1"), the outer join must remain - 
otherwise you get the or-sum-anomaly.

> My hope is that you could indeed drop significant portions of it
> without the existence guards.
> 
> BTW, feel free to add data and tests to the
> NHibernate.Linq.Development project.  I'll quickly integrate pull
> requests.

... only if I need different data, then I have to learn to work with EF ... 
wouldn't be that bad!!!, but - you know - that thing called time ... I'll try!

Regards
Harald

-- 
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de

Reply via email to