mg,

if you think there's any commutative operator in Groovy, you are in for a very 
nasty surprise.

There's not, never has been, and — unless some future release decides to very 
seriously break backward compatibility — never could be.

Consider e.g.,

===
741 /tmp> <q.groovy
class foo {
  def plus(o) { return "foo+$o" }
}
class bar {
  def plus(o) { return "bar+$o" }
}
def a=foo.newInstance(),b=bar.newInstance()
println "a+b=${a+b}"
println "b+a, on the other hand,=${b+a}"
742 /tmp> /usr/local/groovy-3.0.0-alpha-3/bin/groovy q
WARNING: Using incubator modules: jdk.incubator.httpclient
a+b=foo+bar@56de6d6b
b+a, on the other hand,=bar+foo@4fcc0416
743 /tmp> 
===

All the best,
OC

> On 18 Aug 2018, at 7:07 PM, mg <[email protected]> wrote:
> 
> That seems quite inconsistent to me, a bit like the a.equals(b) not implying 
> b.equals(a) inconsistency. I uphold that the order of execution of 
> commutative operations should not influence the result (think of automatic 
> parallelization, where you could get an indeterministic bug if the result 
> depends on the order of execution).
> 
> That Objective-C does not have a convention to at least try to guarantee 
> commutative method nil consistency is surprising (maybe you could supply an 
> example why this does not pose a problem in practice).
> 
> I think we can agree we can safely ignore the case of someone explicitely 
> throwing an NPE in a method... ;-)
> 
> @null.each { ... } : That would be my expectation also, but afaik null.each 
> behavior in dynamic Groovy has been around long before Elvis entered the 
> building...
> 
> Cheers,
> mg
> 
> 
> 
> -------- Ursprüngliche Nachricht --------
> Von: "ocs@ocs" <[email protected]>
> Datum: 16.08.18 04:39 (GMT+01:00)
> An: [email protected]
> Betreff: Re: suggestion: ImplicitSafeNavigation annotation
> 
> mg,
> 
>> On 15 Aug 2018, at 11:15 PM, mg <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> But in addition to this problem, you have the problem that for at least 
>> every commutative* function/operator (plus, times, etc), you have to make 
>> sure that
>> 
>> notNullVariable.commutativeFunctionOrOperator(null)
>> 
>> must now return null, since
> 
> If I really wanted to completely get rid of NPE, I would have to.
> 
> Nevertheless,
> 
> (a) it would be a vain effort; any called method could e.g., throw NPE 
> explicitly if it decides to. No remedy for that!
> (b) it is a normal way of things, just consider
> 
> foo?.bar(baz)
> 
> where baz happens to be null, foo does not, and foo.bar() does throw NPE upon 
> a null argument. “Safe navigaiton” not so safe in fact! :) It is ugly, but 
> inevitable.
> 
> Luckily, in practice, it is not a problem, for it does not happen often.
> 
>> Since you cannot know if an arbitrary function is commutative, the function 
>> itself would need to implement the correct behavior of returning null in 
>> that case. My impression is that it seems exceedingly hard to bend a 
>> null-semantic language into a nil-direction, if it has not been built that 
>> way to begin with.
> 
> Actually this applies to ObjC either: although 
> “nil.whatever(whichever_value)“ is guaranteed by the runtime to be an empty 
> nil-returning operation, “foo.whatever(bar)” for a non-nil foo definitely can 
> throw upon a nil bar (it would not be NPE, which does not exist there, but it 
> might be e.g., an InvalidArgumentException).
> 
> No way to get rid of that — and no problem in practice either.
> 
>> Apart from that I do not see the advantage you claim right now, and to me it 
>> is a bug that in dynamic Groovy null.each { ... } simply does nothing (in 
>> static Groovy this throws a NPE as expected), but I reserve judgement until 
>> I see your larger side by side example :-)
> 
> I do not know the implementation and behaviour myself, but conceptually, I do 
> believe that
> 
> - null.each { ... } should NPE
> - null?.each { ... } should silently do nothing
> 
> quite regardless of static/dynamic. As always, I might be overlooking 
> something of importance.
> 
> All the best.,
> OC
> 
>> 
>> -------- Ursprüngliche Nachricht --------
>> Von: "ocs@ocs" <[email protected] <mailto:[email protected]>>
>> Datum: 15.08.18 19:53 (GMT+00:00)
>> An: [email protected] <mailto:[email protected]>
>> Betreff: Re: suggestion: ImplicitSafeNavigation annotation
>> 
>> Eric,
>> 
>>> On 15 Aug 2018, at 4:14 PM, Milles, Eric (TR Technology & Ops) 
>>> <[email protected] <mailto:[email protected]>> 
>>> wrote:
>>> Does your requested change amount to setting the "isSafe" condition on all 
>>> PropertyExpression instances in a block?
>> 
>> That has been the original idea ages ago, when I have started playing with 
>> that.
>> 
>> It did not prove quite feasible: you cannot “safe” operators this way, there 
>> are those ugly quirks of super?.whatever etc. I have mentioned before...
>> 
>>> That is the AST equivalent of adding the '?' to each '.'
>> 
>> Quitte, only some of '.'s do not take '?' gently and sport a weird results, 
>> from an insane (though understandable) runtime behaviour like 
>> “null?.is(null)" sports up through exceptions compile-time to (at least to 
>> me not understandable) Verity Error caused by super?.anything.
>> 
>> Thanks and all the best,
>> OC
>> 
>>> 
>>> From: ocs@ocs <[email protected] <mailto:[email protected]>>
>>> Sent: Wednesday, August 15, 2018 7:55 AM
>>> To: [email protected] <mailto:[email protected]>
>>> Cc: [email protected] <mailto:[email protected]>
>>> Subject: Re: suggestion: ImplicitSafeNavigation annotation
>>>  
>>> H2,
>>> 
>>>> On 15 Aug 2018, at 9:47 AM, [email protected] <mailto:[email protected]> wrote:
>>>> 
>>>> OCS, your reference to Objective-C is interesting, as is your expectation 
>>>> for your code to encounter null values often.
>>> 
>>> In my experience, a null is (should be, at the very least) a first-class 
>>> citizen, value which simply means “there is no object there”. In normal 
>>> code it happens all the time, e.g., if you check a map with a key which 
>>> happens not to be there, or if you use find with a condition which happens 
>>> not to match any of the items in the list, etc.
>>> 
>>>> I may be old school, but I think the semantics of null values is 
>>>> interesting, as is the semantics of safe navigation.
>>> 
>>> Actually, all this NPE stuff (and its consequences) is pretty new-school :)
>>> 
>>> The aforementioned Objective C (of eighties!) has a couple of very simple 
>>> rules:
>>> - wherever an expression returns an object, it may return a nil;
>>> - whenever a message[*] is sent to a nil, it is a very quick and efficient 
>>> empty operation, whose return value is again nil.
>>> 
>>> [*] in ObjC, you use an object exclusively[**] through sending messages: 
>>> you send a message without any argument for a property getter, you send a 
>>> message with one argument for a property setter, you send a message with an 
>>> arbitrary number of arguments for a method call, etc.
>>> [**] but for the access to instance variables (more or less “fields”), 
>>> which is sort-of similar to plain-C structs, and happens from the declaring 
>>> class code only, usually is limited to the property accessor methods and 
>>> never used outside of them.
>>> 
>>> Whilst I do completely appreciate that tastes and experiences do differ, 
>>> well, myself, in thirty-odd years of using this paradigm daily and after an 
>>> uncounted zillions source lines in projects some of which have 25-odd-year 
>>> lifespan, were made for NeXTSTEP ages ago and are maintained for macOS of 
>>> today, I am yet to find any drawback of this approach. Cases when a bug has 
>>> been caused by unintended and uncaught sending a message to a nil are 
>>> extremely rare (myself I can recall about three of such cases in all those 
>>> years), whilst the advantages for code readability, conciseness, and 
>>> robustness are tremendous.
>>> 
>>>> Safe navigation addresses the matter of nested if statements, perfectly in 
>>>> my opinion.
>>> 
>>> Absolutely. Nevertheless, there's absolutely no point why it should be 
>>> limited to methods — compare e.g., the following case (very simple and thus 
>>> a bit artificial to keep concise):
>>> 
>>> ===
>>> interface TreeNode {
>>>   TreeNode createChildIfPossible(String name); // if possible, creates and 
>>> returns a new named child; null if not possible
>>> }
>>> void makeSubtreeIfPossible(TreeNode tn) { // here, we do not care whether 
>>> successful; just want to make as big a subtree as possible
>>>   def foo=tn?.createChildIfPossible('foo')
>>>   foo?.createChildIfPossible('bar')?.createChildIfPossible('baz')
>>>   
>>> foo?.createChildIfPossible('bax')?.createChildIfPossible('baz')?.createChildIfPossible('last')
>>> }
>>> ===
>>> 
>>> Thanks to ?. we have dodged the necessity of super-ugly ifs, but still, the 
>>> result is sort of at the ugly javaish side. Can we get groovier? Well of 
>>> course we can:
>>> 
>>> ===
>>> class TreeNode {
>>>   def leftShift(object) { this.createChildIfPossible(object) }
>>> }
>>> void makeSubtreeIfPossible(TreeNode tn) {
>>>   def foo=tn?<<'foo'
>>>   foo?<<'bar'?<<'baz'
>>>   foo?<<'bax'?<<'baz'?<<'last'
>>> }
>>> ===
>>> 
>>> Oh, oops! We can't do that, for we do not have “safe navigation” for 
>>> operators, and thus, if we want to use the << for adding a child, we would 
>>> have to get back to ugly Javaish ifs, which I would not dare to show lest 
>>> some reader might get sick :)
>>> 
>>>> What's more, it is explicit in terms of where it applies - you can combine 
>>>> safe navigation with "unsafe navigation", allowing NPE's to be thrown and 
>>>> nulls to propagate where appropriate.
>>> 
>>> Agreed, and where one needs just an occasional null-propagation, it's 
>>> perfect (or, as the example above shows, would be, if it could be used with 
>>> all the operators instead of just with the method call, property access and 
>>> indexing ones).
>>> 
>>> Nevertheless, there are cases where one needs the null-propagation not just 
>>> occasionally, but very often (if not exclusively) in a piece of code — do 
>>> not all the ?'s in the examples above look ugly? And even if you like them, 
>>> they very definitely make the code highly fragile: it is very easy and 
>>> quite probable to simply forget one or two of them, getting thus one 
>>> unwanted, uncaught and potentially disastrous NPE (essentially in random 
>>> based on the data, so testing might not help to find&fix the culprit). You 
>>> would rather have to add a try/catch harness, which in this case creates a 
>>> boilerplate code. We should do without boilerplate in Groovy, in my opinion!
>>> 
>>> On the other hand, this would be clear, concise, completely 
>>> intention-revealing, and completely robust:
>>> 
>>> ===
>>> @ImplicitSafeNavigation(true) void makeSubtreeIfPossible(TreeNode tn) {
>>>   def foo=tn<<'foo'
>>>   foo<<'bar'<<'baz'
>>>   foo<<'bax'<<'baz'<<'last'
>>> }
>>> ===
>>> 
>>>> Unless the same explicit control can be exerted over arithmetic / other 
>>>> expressions, I think those two concepts cannot be compared.
>>> 
>>> Seems to me it's just one concept, not two of them.
>>> 
>>> It has been available from the very beginning for two kinds of expressions: 
>>> a method call and a property getter.
>>> 
>>> Lately, it has been extended to another kind of expression, namely, the 
>>> indexing.
>>> 
>>> It would be only consistent and reasonable to extend the thing to all 
>>> expressions without an exception.
>>> 
>>> Regardless whether that happens or not, still, it is only one and the same 
>>> concept; we are not debating any other one, but just an extent of its 
>>> availability. Especially in Groovy, where... oh, see please below :)
>>> 
>>>> I also think any assumption about the semantics of null values are 
>>>> problematic. In particular, assuming that any null value in an expression 
>>>> should be propagated is not obvious to me.
>>> 
>>> To me, it seems very obvious, completely natural and quite intuitive — 
>>> especially in Groovy where (unlike many other languages) the “expression 
>>> operators” are essentially just a convenience syntax sugar for plain method 
>>> calls (i.e., “foo<<bar” is nothing but a convenience shorthand for 
>>> equivalent but ugly “foo.leftShift(bar)”, “foo+bar” for “foo.plus(bar)”, 
>>> and so forth).
>>> 
>>> Thanks and all the best,
>>> OC
>>> 
>>>> Den 2018-08-15 04:18, skrev ocs@ocs:
>>>>> mg,
>>>>>> On 15 Aug 2018, at 3:26 AM, mg <[email protected] 
>>>>>> <mailto:[email protected]>> wrote:
>>>>>> Fair enough (I am typing this on my smartphone on vacation, so keep
>>>>>> samples small; also (your) more complex code samples are really hard
>>>>>> to read in my mail reader). It still seems to be a big paradigm
>>>>>> change
>>>>> I might be missing something of importance here, but I can't see any
>>>>> paradigm change; not even the slightest shift.
>>>>> The only change suggested is that one could — in the extent of one
>>>>> needs that, which would self-evidently differ for different people —
>>>>> decide whether the “safe” behaviour is explicitly triggered by
>>>>> using the question-mark syntax, or whether it is implicit.
>>>>>> since regular Java/Groovy programs typically have very little null
>>>>>> values
>>>>> The very existence of ?. and ?[] suggests it is not quite the case —
>>>>> otherwise, nobody would ever bother designing and implementing them.
>>>>>> so am not convinced this is worth the effort (and as Jochen pointed
>>>>>> out, there will still be cases where null will just be converted to
>>>>>> "null").
>>>>> Are there? Given my limited knowledge, I know of none such.
>>>>> “null?.plus('foo')” yields a null, and so — for a consistency
>>>>> sake — very definitely should also “null?+'foo'” and
>>>>> “@ImplicitSafeNavigation ... null+foo”, had they existed.
>>>>>> What I would suggest instead is considering to introduce nil,
>>>>>> sql_null, empty, ... as type agnostic constants in addition to the
>>>>>> existing null in Groovy. That way you could use e.g. nil in your
>>>>>> code, which by definition exhibits your expected behavior, but it
>>>>>> would make the usage more explicit, and one would not need to
>>>>>> switch/bend the existing null semantics...
>>>>> That's a nice idea; alas, so that it is viable, one would also have to
>>>>> be able to set up which kind of null is to be returned from
>>>>> expressions like “aMap['unknownkey']“ or “list.find {
>>>>> never-matches }” etc.
>>>>> Thus, instead of my “@ImplicitSafeNavigation(true)” you would have
>>>>> to use something like “@DefaultNullClass(nil)” — and instead of
>>>>> “@ImplicitSafeNavigation(false)” you would need something like
>>>>> “@DefaultNullClass(null)”.
>>>>> Along with that, you would need a way to return “the current default
>>>>> null” instead of just null; there would be a real problem with a
>>>>> legacy code which returns null (but should return “the current
>>>>> default null” instead), and so forth.
>>>>> That all said, it definitely is an interesting idea worth checking;
>>>>> myself, though, I do fear it would quickly lead to a real mess (unlike
>>>>> my suggestion, which is considerably less flexible, but at the same
>>>>> moment, very simple and highly intuitive).
>>>>> Thanks and all the best,
>>>>> OC
>>>>>> -------- Ursprüngliche Nachricht --------
>>>>>> Von: "ocs@ocs" <[email protected] <mailto:[email protected]>>
>>>>>> Datum: 15.08.18 00:53 (GMT+00:00)
>>>>>> An: [email protected] <mailto:[email protected]>
>>>>>> Betreff: Re: suggestion: ImplicitSafeNavigation annotation
>>>>>> mg,
>>>>>>> On 15 Aug 2018, at 1:33 AM, mg <[email protected] 
>>>>>>> <mailto:[email protected]>> 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" <[email protected] <mailto:[email protected]>>
>>>>>> Datum: 14.08.18 23:14 (GMT+00:00)
>>>>>> An: [email protected] <mailto:[email protected]>
>>>>>> Betreff: Re: suggestion: ImplicitSafeNavigation annotation
>>>>>> mg,
>>>>>> On 14 Aug 2018, at 11:36 PM, mg <[email protected] 
>>>>>> <mailto:[email protected]>> 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" <[email protected] <mailto:[email protected]>>
>>>>>> Datum: 14.08.18 17:46 (GMT+00:00)
>>>>>> An: [email protected] <mailto:[email protected]>
>>>>>> Betreff: Re: suggestion: ImplicitSafeNavigation annotation
>>>>>> Jochen,
>>>>>> On 14 Aug 2018, at 6:25 PM, Jochen Theodorou <[email protected] 
>>>>>> <mailto:[email protected]>>
>>>>>> 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
>> 
> 

Reply via email to