groovy-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "ocs@ocs" <...@ocs.cz>
Subject Re: suggestion: ImplicitSafeNavigation annotation
Date Tue, 14 Aug 2018 23:53:16 GMT
mg,

> On 15 Aug 2018, at 1:33 AM, mg <mgbiz@arscreat.com> wrote:
> 
> That's not how I meant my sample eval helper method to be used :-)
> 
> (for brevity I will write neval for eval(true) here)
> 
> What I meant was: How easy would it be to get a similar result to what you want, by wrapping
a few key places (e.g. a whole method body) in your code in neval { ... } ? Evidently that
would just mean that any NPE inside the e.g. method would lead to the whole method result
being null. 

Which is a serious problem. Rarely you want „a whole method be skipped  (and return null)
if anything inside of it happens to be null“. What you normally want is the null-propagation,
e.g.,

def foo=bar.baz[bax]?:default_value;
... other code ...

The other code is always performed and never skipped (unless another exception occurs of course);
but the null-propagation makes sure that if bar or bar.baz happens to be a null, then default_value
is used. And so forth.

> To give a simple example:
> 
> final x = a?.b?.c?.d
> 
> could be written as
> 
> final x = neval { a.b.c.d }

Precisely. Do please note that even your simple example did not put a whole method body into
neval, but just one sole expression instead. Essentially all expressions — often sub-expressions,
wherever things like Elvis are used — would have to be embedded in nevals separately. Which
is, alas, far from feasible.

> Of course the two expressions are not semantically identical, since neval will transform
any NPE inside evaluation of a, b, c, and d into the result null - but since you say you never
want to see any NPEs...

That indeed would not be a problem.

> (The performance of neval should be ok, since I do not assume that you expect your code
to actually encounter null values, and accordingly NPEs, all the time)

This one possibly would though: I do expect my code to encounter null values often — with
some code, they might well be the normal case with a non-null an exception. That's precisely
why I do not want NPEs (but the quick, efficient and convenient null-propagation instead)
:)

Thanks and all the best,
OC

> -------- Ursprüngliche Nachricht --------
> Von: "ocs@ocs" <ocs@ocs.cz>
> Datum: 14.08.18 23:14 (GMT+00:00)
> An: dev@groovy.apache.org
> Betreff: Re: suggestion: ImplicitSafeNavigation annotation
> 
> mg,
> 
>> On 14 Aug 2018, at 11:36 PM, mg <mgbiz@arscreat.com <mailto:mgbiz@arscreat.com>>
wrote:
>> 
>> I am wondering: In what case does what you are using/suggesting differ significantly
from simply catching a NPE that a specific code block throws and letting said block evaluate
to null in that case:
>> 
>> def eval(bool nullSafeQ, Closure cls) {
>>   try {
>>     return cls()
>>   }
>>   catch(NullPointerException e) {
>>     if(nullSafeQ) {
>>       return null
>>     }
>>     throw e
>>   }
>> }
> 
> Conceptually, not in the slightest.
> 
> In practice, there's a world of difference.
> 
> For one, it would be terrible far as the code cleanness, fragility and readability are
concerned — even worse than those ubiquitous question marks:
> 
> === the code should look, say, like this ===
> @ImplicitSafeNavigation def foo(bar) {
>   def x=baz(bar.foo)?:bax(bar.foo)
>   x.allResults {
>     def y=baz(it)
>     if (y>1) y+bax(y-1)
>     else y–bax(0)
>   }
> }
> === the eval-based equivalent would probably look somewhat like this ===
> def foo(bar) {
>   def x=eval(true){baz(eval(true){bar.foo})?:bax(bar.foo)}
>   eval(true){
>     x.allResults {
>       def y=eval(true){baz(it)}
>       if (y>1) eval(true){y+bax(y-1)}
>       else eval(true){y–bax(0)}
>     }
>   }
> }
> ===
> 
> and quite frankly I am not even sure whether the usage of eval above is right and whether
I did not forget to use it somewhere where it should have been. It would be ways easier with
those question marks.
> 
> Also, with the eval block, there might be a bit of a problem with the type information:
I regret to say I do not know whether we can in Groovy declare a method with a block argument
in such a way that the return type of the function is automatically recognised by the compiler
as the same type as the block return value? (Definitely I don't know how to do that myself;
Cédric or Jochen might, though ;))
> 
> Aside of that, I wonder about the efficiency; although premature optimisation definitely
is a bitch, still an exception harness is not cheap if an exception is caught, I understand.
> 
>> (It feels a bit like what you wants is tri-logic/SQL type NULL support in Groovy,
not treating Java/Groovy null differently...)
> 
> In fact what I want is a bit like the Objective-C simple but very efficient and extremely
practical nil behaviour, to which I am used to and which suits me immensely.
> 
> Agreed, the Java world takes a different approach (without even the safe navigation where
it originated!); I have tried to embrace that approach a couple of times, and always I have
found it seriously lacking.
> 
> I do not argue that the null-propagating behaviour is always better; on the other hand,
I do argue that sometimes and for some people it definitely is better, and that Groovy should
support those times and people just as well as it supports the NPE-based approach of Java.
> 
> Thanks and all the best,
> OC
> 
>> -------- Ursprüngliche Nachricht --------
>> Von: "ocs@ocs" <ocs@ocs.cz <mailto:ocs@ocs.cz>>
>> Datum: 14.08.18 17:46 (GMT+00:00)
>> An: dev@groovy.apache.org <mailto:dev@groovy.apache.org>
>> Betreff: Re: suggestion: ImplicitSafeNavigation annotation
>> 
>> Jochen,
>> 
>>> On 14 Aug 2018, at 6:25 PM, Jochen Theodorou <blackdrag@gmx.org <mailto:blackdrag@gmx.org>>
wrote:
>>> Am 14.08.2018 um 15:23 schrieb ocs@ocs:
>>>> H2,
>>>>> However, “a+b” should work as one would expect
>>>> Absolutely. Me, I very definitely expect that if a happens to be null, the
result is null too. (With b null it depends on the details of a.plus implementation.)
>>> 
>>> the counter example is null plus String though
>> 
>> Not for me. In my world, if I am adding a string to a non-existent object, I very
much do expect the result is still a non-existent object. Precisely the same as if I has been
trying to turn it to lowercase or to count its character or anything.
>> 
>> Whilst I definitely do not suggest forcing this POV to others, to me, it seems perfectly
reasonable and 100 per cent intuitive.
>> 
>> Besides, it actually (and expectably) does work so, if I use the method-syntax to
be able to use safe navigation:
>> 
>> ===
>> 254 /tmp> <q.groovy 
>> String s=null
>> println "Should be null: ${s?.plus('foo')}"
>> 255 /tmp> /usr/local/groovy-2.4.15/bin/groovy q
>> WARNING: An illegal reflective access operation has occurred
>> ... ...
>> Should be null: null
>> 256 /tmp> 
>> ===
>> 
>> which is perfectly right. Similarly, a hypothetical “null?+'foo'” or “@ImplicitSafeNavigation
... null+foo” should return null as well, to keep consistent.
>> 
>> (Incidentally, do you — or anyone else — happen to know how to get rid of those
pesky warnings?)
>> 
>> Thanks and all the best,
>> OC
>> 
>> 
>> 
> 


Mime
View raw message