Re: Do you consider these different or the same?
by tmoertel (Chaplain) on Jul 01, 2005 at 16:50 UTC
|
You are asking the wrong questions. Instead, ask –
- What is the purpose of Test::More::is_deeply?
- What, then, should its semantics be?
My answer to the first is that is_deeply's purpose is to
make it easy to test complex observed values by comparing them with given expected
values.
My answer to the second, then, is that is_deeply should
consider the observed and expected values to be equivalent if both
have the same structure and subvalues, ignoring sharing. (Remember, I am
talking semantics here, not implementation.)
Why ignore sharing? Because it rarely matters in tests. By
ignoring it we make the common case easier for testers, who frequently
hand-roll expected values. This goes back to the purpose of
is_deeply; convenience matters.
As to your questions, then, here is how I answer. In the first
case, $ar1 and $ar2 are equivalent. Both have
the same structure and subvalues – [{},{}].
In the second case, they are not equivalent; [{a=>'foo'},{a=>'foo'}] differs from [{a=>'foo'},{}].
Cheers, Tom
| [reply] |
|
Exactly.
Also note that the name "is_deeply" comes from the is() function that only cares about eq. So "is_deeply" is meant to convey applying eq deeply between two structures. Something that does more than that needs a different name if it is part of Test::More.
The "deeply" part means that $a vs. "$a" fail the test if $a is a reference because only leaves are compared.
| [reply] |
Re: Do you consider these different or the same?
by hv (Prior) on Jul 01, 2005 at 11:08 UTC
|
I do not see them as deep copies of each other. Whether I'd see them as being "equivalent" would depend on context - I guess I'd think of them as "equivalent up to aliasing", which would be "equivalent enough" for some purposes and not for others.
After the operation, I'd no longer see them as equivalent at all.
Hugo
| [reply] |
|
| [reply] |
Re: Do you consider these different or the same?
by fergal (Chaplain) on Jul 01, 2005 at 12:04 UTC
|
I guess it depends on whether I'm going to change the data afterwards. It also depends on why I'm comparing them.
Let's say I'm testing my date and time library. All objects are supposed to be immutable points in in time, numbers of hours, dates etc and
In this case since everything's immutable I don't care about identities, only values. So when I'm testing I'll have a bunch of expected values and a bunch of result values. I might reuse the same date object multiple times in the expected values even though the result values have multiple different occurrences of the same date value. I don't want that to be a problem.
I don't think there's a right anwer, it should be possible to compare in both ways.
Finally in relation to is_deeply, it considers
$a=[];
is_deeply($a, "$a");
to be a pass. It also compares string-overloaded objects as strings and refuses to look inside and it completely ignores
blessings. I think is_deeply should just keep doing exactly what it's doing. I and others know exactly what it's doing. Sometimes I use it, mostly I don't but changing its behaviour at this stage risks breaking tests and causing confusion.
| [reply] [d/l] |
|
I might reuse the same date object multiple times in the expected values even though the result values have multiple different occurrences of the same date value. I don't want that to be a problem.
Then you dont want a deep comparison, you want a shallow comparison. In fact you want the proposed 'looks_like()' method.
Finally in relation to is_deeply, it considers
$a=[];
is_deeply($a, "$a");
to be a pass.
Actually it doesn't. At least not in version 0.60
I and others know exactly what it's doing. Sometimes I use it, mostly I don't but changing its behaviour at this stage risks breaking tests and
My position is that we can eliminate the confusion by making is_deeply() do a proper deep structural comparison and provide a different routine (that would conveniently share its implementation with is_deeply).
Long term I believe this would clear up a LOT of misunderstanding about references that I see even experienced Perl programmers making. Misunderstanding such as is_deeply() considering $ar1 and $ar2 to be the same, which is just totally wrong.
my ($x,$y,$ar1);
$x=\$y;
$y=\$x;
$ar1=[$x,$y];
my $ar2;
$ar2->[0]=\$ar2->[1];
$ar2->[1]=\$ar2->[0];
is_deeply($ar1,$ar2); #this is wrong, wrong, wrong.
If is_deeply()'s behaviour doesn't change then it should have very large warnings on it that it doesnt do what it claims to do (that is checking if the objects are deep copies of each other.)
---
$world=~s/war/peace/g
| [reply] [d/l] [select] |
|
Then you dont want a deep comparison, you want a shallow comparison. In fact you want the proposed 'looks_like()' method.
Not if my date looks like {day => 5, month => 11, year => 2005} because I need to look inside again. You might argue that the date shouldn't look like that but I just chose date as an example of where you have what is really a value which who's identity is irrelevant. The only reason it even has an identity is because Perl (and many other languages) do not allow you to represent structured values except by using what you might call "anonymous variables". Haskell, relational databases and probably lots of other pure functional languages on the other hand do not force this although they do allow it if you explicitly request it.
Actually it doesn't. At least not in version 0.60
It didn't do that in ver 0.39 (or thereabouts) either but that was reverted because it also fixed (I considered it fixed anyway) the overloaded reference behaviour. Version 0.60 might undo that but it also removes comparison of subrefs and seems to be completely broken for overloaded values, so I wouldn't be too sure that that will stay that way for very long.
Assuming the docs are correct though is_deeply will still never look inside an overloaded object which to my mind is a far bigger sin against deepness than this problem, also not checking blessedness means that 2 deeply equal values will not necessarily react in the same way to method calls (in fact one of them may not even be an object).
I've spent plenty of time arguing about the right thing for is_deeply to do but I stopped when I was convinced that is_deeply is not going anywhere new and that the author was not interested in trying to make it go anywhere new. It seems that its author has now changed his mind somewhat (with the sub refs thing) but I actually think he was right in the first place. It does what it does and it's been doing that for years now. Changing it's behaviour now makes no sense to me.
| [reply] [d/l] |
|
is_deeply()
is_truly()
is_madly()
Hugo | [reply] [d/l] |
|
Re: Do you consider these different or the same?
by spurperl (Priest) on Jul 01, 2005 at 11:32 UTC
|
How "equivalent" do you mean ? To resolve such philosophical questions, Common Lisp has no less than 5 equality operators that address various "equality" questions.
See here for example, perhaps you'll find something to inspire an answer ? | [reply] |
|
The one that looks closes is 'equal' however, its not clear how it defines "isomorphic". Assuming it defines it the same way I would (a reference used twice is different from two references, even if the content of the references is 'equal').
---
$world=~s/war/peace/g
| [reply] |
|
| [reply] |
Re: Do you consider these different or the same?
by brian_d_foy (Abbot) on Jul 01, 2005 at 17:57 UTC
|
The Test::More is_deeply() test acts like all of the other tests in Test::More by comparing values, not references. If two independent variables (meaning I change one and the other stays as it was) happen to have the same values, I expect is_deeply to pass.
What I don't expect, and don't want to test with is_deeply, is that the same operations will produce the same results.
I can see what you want to test, but I don't think is_deeply should be the thing to do that. It lives in its own community of tests in Test::More that all have the same idea and the same way of looking at things. If you changed how is_deeply works you have to change a lot more in Test::More.
In fairness, there are notes in the is_deeply docs pointing at other modules. If those don't work for you, make another one that does and then send in a doc patch. (Along with that doc patch could be something to remove the word "equivalent" from the docs, since I think that's where the confusion starts).
--
brian d foy <brian@stonehenge.com>
| [reply] |
Re: Do you consider these different or the same?
by hardburn (Abbot) on Jul 01, 2005 at 12:36 UTC
|
When I use is_deeply() in tests, I'm usually taking one datastructure that was returned by the code being tested, and another that I built that represents what I expect the code to return. Under that usage, is_deeply() needs to see both the posted datastructures as equal.
I suspect that the various opinions on this issue will reflect how people use is_deeply() in practice.
"There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.
| [reply] [d/l] [select] |
|
Under that usage, is_deeply() needs to see both the posted datastructures as equal.
I dont understand. You want is_deeply() to gloss over the fact that your hand constructed example is different from what it returns? Why is this good? And please before you use the example that Schwern did in a post, think about somethign like this:
my %expect_hash=(a=>1,b=>2);
my @expect=({%expect_hash},{%expect_hash});
#versus
my @copies=(\%expect_hash,\%expect_hash);
Is the extra char too much price to pay for is_deeply() doing what its name says it does? (this isn't intended as a snarky question, I really am curious.)
---
$world=~s/war/peace/g
| [reply] [d/l] |
|
(I don't see the Schwern post you mentioned in this thread. Is it someplace else?)
All I see for is_deeply()'s documentation is:
Similar to is(), except that if $this and $that are hash or array references, it does a deep comparison walking each data structure to see if they are equivalent. If the two structures are different, it will display the place where they start differing.
Which, to me, just means it will make sure all the values in the nested data structure are the same. Wheather the actual references are the same is irrelevent in most of the code I've encountered. Of course, there are always exceptions, so a seperate sub that has a more exact definition of equivilence is likely needed.
"There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.
| [reply] [d/l] |
Re: Do you consider these different or the same?
by tlm (Prior) on Jul 01, 2005 at 12:16 UTC
|
This question is a bit philosophical... (Are you the same person you were 10 years ago? 10 months ago? 10 minutes ago?) I suppose that one could get at it via "majority rule", but I think the more fruitful approach is the one used by (for example) Scheme, which recognizes several levels of equality. (For Scheme these are, in increasing degree of stringency: equal?, eqv?, and eq?.) This approach recognizes the fact that different tasks demand different criteria for equality, instead of trying to shoehorn every situation into a single criterion for equality.
| [reply] |
|
| [reply] |
Re: Do you consider these different or the same?
by itub (Priest) on Jul 01, 2005 at 13:54 UTC
|
my $ar1=[$x,$x];
my $ar2=[$y,$z];
But these are the same:
my $ar3=[$x,$x];
my $ar4=[$y,$y];
That is, the comparison should care about the addresses of the references, but it should care about loops. Another way of looking at it is: deeply equivalent references should have the same canonical representation when dumped. Compare the following:
# $ar1
$VAR1 = [
{},
$VAR1->[0]
];
# $ar2
$VAR1 = [
{},
{}
];
# $ar3
$VAR1 = [
{},
$VAR1->[0]
];
# $ar4
$VAR1 = [
{},
$VAR1->[0]
];
| [reply] [d/l] [select] |
Re: Do you consider these different or the same?
by Arunbear (Prior) on Jul 01, 2005 at 11:38 UTC
|
print "@$ar1\n@$ar2\n";
produces:
HASH(0x224fc8) HASH(0x224fc8)
HASH(0x22507c) HASH(0x225088)
so deep copies they are not. | [reply] [d/l] [select] |
|
Although "deep copy" shows up in the original post, I think it's misplaced. The is_deeply docs don't say anything about testing whether or not they are deep copies. The other things in Test::More check for the same values, not the same references, and that's what I expect is_deeply to do, and for the "deeply" only to denote its descent into references to get their values.
--
brian d foy <brian@stonehenge.com>
| [reply] |
Re: Do you consider these different or the same?
by Hena (Friar) on Jul 01, 2005 at 12:30 UTC
|
I think I should read more on what is Test::More::is_deeply() is. But without reading that I would say no. They just look the same but doing
$ar1->[0]{a}='foo';
$ar2->[0]{a}='bar';
Then they are not the same anymore.
After quick look at what is_deeply is, i don't know. It should explain what the subroutine is after. Eg. which is it
1. Do these look the same to outside (at this particular moment)?
2. Are these exatly identical in all ways?
Depending what you want take your pick. But if both of these are wanted, then make two subroutines. | [reply] [d/l] |
Re: Do you consider these different or the same?
by duelafn (Parson) on Jul 02, 2005 at 04:05 UTC
|
I think that it is reasonable for a test module to consider them different since it is the safer approach (the whole point of testing in the first place). It is much easier (better) for someone to notice their test failing and replace is_deeply with is_deeply_not_so_strict (or whatever) than for someone to find out through a bug report that their test wasn't strict enough.
| [reply] [d/l] [select] |
|
| [reply] |
Re: Do you consider these different or the same?
by bageler (Hermit) on Jul 02, 2005 at 00:47 UTC
|
I'd say six in one hand, half dozen in the other. Different but equivalent. | [reply] |
Re: Do you consider these different or the same?
by halley (Prior) on Jul 04, 2005 at 01:20 UTC
|
When it comes to deep comparisons, I would define "equivalent" as being equal by value, and "identical" as sharing the identity.
In my experience, it's RARELY useful to compare things as being identical beyond the top level scalar. It's COMMONLY useful to compare things as being equivalent to all depths.
And lastly, if you like checking if things are identical, then you'll probably love seeing if things are equivalent to a certain depth, and identical below that depth. For example, { $x } is not identical to { $x }, because the curly braces produce two equivalent hashrefs with identical contents.
-- [ e d @ h a l l e y . c c ]
| [reply] [d/l] |