Issue Details (XML | Word | Printable)

Key: GROOVY-1948
Type: Bug Bug
Status: Closed Closed
Resolution: Won't Fix
Priority: Major Major
Assignee: Jochen Theodorou
Reporter: Michael Fraenkel
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
groovy

== doesn't behave properly

Created: 22/Jun/07 12:39 PM   Updated: 26/Jun/07 10:38 AM
Component/s: None
Affects Version/s: 1.0, 1.1-beta-1
Fix Version/s: 1.1-beta-2

Time Tracking:
Not Specified


 Description  « Hide
When one writes something like Object == null, the behavior implemented in DefaultTypeTransformation.compareEqual() has 2 shortcomings.

1. If you have a Wrapper instance, the instance rather than the wrappered value is compared. I would have thought that wrapper == whatever is equivalent to wrapper.unwrap() == whatever.

2. Along the same lines, the short circuit check for null value on the left and right side doesn't seem right. It makes sense for the left side, however if the left side is an object, and the right side is null, the object should make the determination. This would fix #1 as well.



 All   Comments   Work Log   Change History      Sort Order: Ascending order - Click to sort in descending order
Jochen Theodorou added a comment - 23/Jun/07 03:09 PM
to 1)
I guess you mean the wrapper defined in Groovy by the wrapper package... could you please post an running example showing this. You should not come into contact with a wrapper unless you write a MetaClass, and in that case it is your task to unwrap. These wrappers are no classic wrappers in the sense that other languages are using, they are not there to represent values of the runtime and to allow faster invocation paths or something like that. they are there to keep a static type during a method call, nothing more... They might become obsolete in 2.0

to 2)
if null==x is ok, then why is x==null not? shouldn't equals be symetric? And why would it fix #1?

I really need a test case to see what you are talking about


Michael Fraenkel added a comment - 23/Jun/07 08:23 PM
A little background would probably help.

We have a tree where every node has a name and potentially a value. We are trying to mask an explicit retrieval behavior. For example, a.b.c = d, would set the node /a/b/c to d, and f = a.b.c would retrieve the value of a.b.c and store it into f.

We ran across Wrappers, which gave us the get() behavior we were looking for. By also implementing GroovyInterceptable, we were able to forward all methods to the value contained in the node.

The only piece of the puzzle we haven't been able to solve is when == is used. In all other cases, unwrap is called as needed or we intercept the method call. == however assumes that there is no special rules for object equality.

We are open to alternative suggestions.


Jochen Theodorou added a comment - 24/Jun/07 04:53 AM
So I am right in thinking that you are using the wrappers outside by yourself? then you are most probably using the wrappers outside their scope. They are just for method invocations, not for properties. Instead of using th wrappers from org.codehaus.groovy.wrapper, you should use your own wrappers... if needed. I mean for storing d in a.b.c you don't need a wrapper. If you tell me in which cases it should give an exception, or if all possible paths are fine, then I can give you a different solution.

Michael Fraenkel added a comment - 24/Jun/07 07:42 AM
All paths "are" valid. The value returned may be null, so there is never an exception. The behavior of Wrappers we liked was the fact that when passed into a method, it was "unwrapped". We haven't found alternative that provides this get() behavior. On set you call setProperty which allows us to do the right thing. For get, there isn't really anything that tips us off to return the value stored at the path. This is the same problem with ==. When you do something like a.b.c == null, we want it to be equivalent to a.b.c.get() == null. But since == just looks at the left hand object, in our case it gets this PathObject rather than PathObject.get()

Jochen Theodorou added a comment - 24/Jun/07 09:53 AM
ok, so I assume you want the following:
def a = new DynamicTreeNode(value:"I am root")
assert a.b == null
assert a.b.c == null
a.b.c = new DynamicTreeNode(value: "/b/c")
assert a.b.c.value == "/b/c"

something like that... the puzzling thing here for me is, that a.b == null, means for me that a.b.c should throw an NPE. On the other hand, what would be about:

def a = new DynamicTreeNode(value:"I am root")
assert a.b != null
assert a.b.value == null
assert a.b.c != null
assert a.b.c.value == null
a.b.c.value = "/b/c"
assert a.b.c.value == "/b/c"
assert a.b.value == null

In this case a.b is not null, thus a.b.c will not throw an NPE. Of course this is a hand made wrapper, but this wrappers comes natural with the DynamicTreeNode class:

class DynamicTreeNode {
    private namedNodes = [:]
    def value
    
    void set(String name, value) {
        namedNodes.put(name,value)
    }
    def get(String name) {
        if (namedNodes.containsKey(name)) return namedNodes.get(name)
        def node = new DynamicTreeNode()
        set(name,node)
        return node
    }
    String toString(){
        "<$value ($namedNodes)>"
    }
}

This is not good enough for you? Btw, get/set like here are called for missing properties. get/setProperty would be called before the properties are handled.


Michael Fraenkel added a comment - 24/Jun/07 12:34 PM
This is conceptually exactly what we are doing except in a different way.
We are trying to avoid the a.b.c.value which would be a.b.c.get() in our model. We are trying to make it transparent. I realize it may not be possible.

If you think of these paths as URIs, a.b doesn't have to exist for a.b.c to return a value.


Jochen Theodorou added a comment - 24/Jun/07 01:05 PM
If I see it as URI, then I would expect, that if the URI a.b.c exists, the URI a.b does exist too. What I don't expect is that the content for the URI in a.b exists. You mix the path and the content of the path.. I mean we could for example also have something like
tree[a.b.c]

that would be very easy to do.... but possibly a bit strange. So I guess there is no real way out of this.. I mean breaking the symmetry of == to make your code working is only a symptom of bigger problems that will follow in other parts... At last I would expect that. I suggest asking on the mailing list, maybe someone there has a better idea, but for now I suggest to close this issue as "Won't fix" if you agree


Michael Fraenkel added a comment - 25/Jun/07 09:50 AM
Go ahead and close it.