Skip Menu |

This queue is for tickets about the List-MoreUtils CPAN distribution.

Report information
The Basics
Id: 40905
Status: resolved
Priority: 0/
Queue: List-MoreUtils

People
Owner: Nobody in particular
Requestors: cebratcher [...] gmail.com
Cc: ether [...] cpan.org
ribasushi [...] leporine.io
AdminCc:

Bug Information
Severity: Important
Broken in: 0.22
Fixed in: (no value)



Subject: Returning undef when none is passed an empty array is counterintuitive
This snippet of code should logically output "foo NOT found in bar": ############################## my @bar = (); if (none {/foo/} @bar) { print "foo NOT found in bar\n"; } else { print "foo found in bar\n" } ############################## It doesn't as none returns undef if the given array is empty. The attached patch fixes this problem. -Clif
Subject: none_empty_array.patch
diff --git a/List-MoreUtils-0.22/MoreUtils.xs b/List-MoreUtils-0.22/MoreUtils.xs index 9011867..595f343 100644 --- a/List-MoreUtils-0.22/MoreUtils.xs +++ b/List-MoreUtils-0.22/MoreUtils.xs @@ -295,7 +295,7 @@ CODE: CV *cv; if (items <= 1) - XSRETURN_UNDEF; + XSRETURN_YES; cv = sv_2cv(code, &stash, &gv, 0); PUSH_MULTICALL(cv); diff --git a/List-MoreUtils-0.22/lib/List/MoreUtils.pm b/List-MoreUtils-0.22/lib/List/MoreUtils.pm index 01a2510..4923fbb 100644 --- a/List-MoreUtils-0.22/lib/List/MoreUtils.pm +++ b/List-MoreUtils-0.22/lib/List/MoreUtils.pm @@ -47,7 +47,7 @@ sub all (&@) { sub none (&@) { my $f = shift; - return if ! @_; + return 1 if ! @_; for (@_) { return 0 if $f->(); } @@ -386,7 +386,8 @@ criterion given through BLOCK. Sets C<$_> for each item in LIST in turn: print "No value defined" if none { defined($_) } @list; -Returns false otherwise, or C<undef> if LIST is empty. +As an empty list will never match the criterion, C<none> returns C<1> if LIST is empty, +returns false otherwise. =item notall BLOCK LIST diff --git a/List-MoreUtils-0.22/t/List-MoreUtils-pp.t b/List-MoreUtils-0.22/t/List-MoreUtils-pp.t index 6382877..234b2aa 100644 --- a/List-MoreUtils-0.22/t/List-MoreUtils-pp.t +++ b/List-MoreUtils-0.22/t/List-MoreUtils-pp.t @@ -54,7 +54,7 @@ BEGIN { $TESTS += 4 } ok(none { !defined } @list); ok(none { $_ > 10000 } @list); ok(!none { defined } @list); - ok(!defined none { }); + ok(none { }); } # notall (16...) diff --git a/List-MoreUtils-0.22/t/List-MoreUtils.t b/List-MoreUtils-0.22/t/List-MoreUtils.t index 299b866..631eccc 100644 --- a/List-MoreUtils-0.22/t/List-MoreUtils.t +++ b/List-MoreUtils-0.22/t/List-MoreUtils.t @@ -63,7 +63,7 @@ BEGIN { $TESTS += 4 } ok(none { !defined } @list); ok(none { $_ > 10000 } @list); ok(!none { defined } @list); - ok(!defined none { }); + ok(none { }); } # notall (15...)
This is resolved in 0.23 which I just uploaded to the CPAN.
On Sun Jul 12 14:47:35 2009, VPARSEVAL wrote: Show quoted text
> This is resolved in 0.23 which I just uploaded to the CPAN.
Note that the 0.23 code line is no longer in the latest release, 0.26. Consequently this and a number of other tickets in this queue are no longer resolved.
Fix reapplied. Fixed in 0.27_03.
I disagree with the made change (any/notall returns false, all returns true - like none). I prefer a reasonable discussion.
On Fri Oct 11 01:09:04 2013, REHSACK wrote: Show quoted text
> I disagree with the made change (any/notall returns false, all returns > true - like none). I prefer a reasonable discussion.
I think the problem here is looking at these in isolation. In order to get a coherent API, let's (whether we actually add the section or not) invent a proposed "what functions do when passed an empty list" section and list each one, its behaviour, and why that behaviour is consistent conceptually. Note that I'm not suggesting we simply rationalise the current behaviour, but instead propose a consistent set of behaviour - so if somebody wants to propose such a section, they should annotate where the behaviour changes from the current. REHSACK, since you asked for the discussion, I hereby nominate you to produce the first proposal to provide a starting point. Well volunteered :D
RT-Send-CC: mst [...] shadowcat.co.uk, leonerd [...] leonerd.org.uk
Okay - my proposal is to let the return value be correct instead of simple. That means: for any, all, none and notall the return value should be undef in case of no items are specified. Why? Because without any item, a decision whether any, all, not all or none item matches or don't matches a specific criteria can't be done. So consequently the return value should transport the information: no decision possible. For boolean evaluation that makes not much difference: if(any { ... } @empty) => not true, skip block if(!any { ... } @empty) => true, execute block Same for the rest ... Cheers, Jens
In many ways this is analogous to the question of sum/product on empty lists. You can draw an analogy between sum and any, by noting the properties of addition vs boolean inclusive-or and the identities 0 and false (and ignoring for this argument the predicate function given to 'any', we'll consider that A,B,C are the boolean results of the predicate function mapped over the original input list): sum A,B,C = A + B + C = 0 + A + B + C sum A = A = 0 + A sum () = 0 any A,B,C = A | B | C = false | A | B | C any A = A = false | A any () = false To have any return false on an empty list is consistent with its description - it failed to find any value in the input list that satisfied its predicate, so it must return false. Turning now to product and all, we can note again the properties of multiplication vs boolean and, and the identities 1 and true: product A,B,C = A * B * C = 1 * A * B * C product A = A = 1 * A product () = 1 all A,B,C = A & B & C = true & A & B & C all A = A = true & A all () = true Having all return true on an empty list is also consistent with its description - it could find no counter-example to the claim "all these values pass the predicate", so it must return true. In all four cases, sum, product, any, all, they are simply the (associative) list reduction of their input lists over their respective binary operator, starting from the identity value of that operator function op id sum + 0 product * 1 any || false all && true This is furthermore hinted at by the example definitions of these functions in terms of the base 'reduce' case: reduce { $a || $predicate->(local $_ = $b) } 0, @list # any reduce { $a && $predicate->(local $_ = $b) } 1, @list # all By way of a final motivation, you can also note the generalisation of DeMorgan's law applied to the any/all: A & B == not( (not A) | (not B) ) all { PREDICATE } LIST == not any { not PREDICATE } LIST For this to remain true even when the LIST is empty, it is required that any and all on empty lists return complementary values - all true, any false. Finally, I don't believe any of these functions should ever return undef - they are boolean predicate tests so should always return a defined boolean value. -- Paul Evans
CC: cebratcher [...] gmail.com, ether [...] cpan.org
Subject: Re: [rt.cpan.org #40905] Returning undef when none is passed an empty array is counterintuitive
Date: Tue, 22 Oct 2013 08:55:16 -0700
To: Jens Rehsack via RT <bug-List-MoreUtils [...] rt.cpan.org>
From: Karen Etheridge <ether [...] cpan.org>
On Tue, Oct 22, 2013 at 03:23:17AM -0400, Jens Rehsack via RT wrote: Show quoted text
> <URL: https://rt.cpan.org/Ticket/Display.html?id=40905 > > > Okay - my proposal is to let the return value be correct instead of simple. > > That means: for any, all, none and notall the return value should be undef in case of no items are specified.
I must note that this is *not* the same as what the versions in List::MoreUtils return (ahead by several years in establishing an API): any: returns FALSE if list is empty all: returns TRUE if list is empty none: returns TRUE if list is empty notall: returns FALSE if list is empty Given that List::Util and List::MoreUtils are often used at the same time, and there are even libraries that combine the two into one (List::AllUtils), I would strongly suggest that the interfaces be kept aligned.
The question was at no point, what has List::MoreUtils in API - the point was, that it has changed without permission and asking back by hijacker. Sorry for inconvinience - but after 10 years you can ask for custom and practice. You're chance here is to argue - and I completely follow LeoNerd's explanation, even if I think there is some truth in my point of view, too. But hijacked custom and practice is - sorry - not a valid reason to keep it.
RT-Send-CC: ether [...] cpan.org, mst [...] shadowcat.co.uk
Resolved by switching to Sub::Exporter and allow users to choose between alias implementation and tassilo's. WIth 0.401 alias implementation will be announced being depreciated and some time later a warning will be issued when using ':all' and even some more time later ':all' will disappear.