Project

General

Profile

Feature #12110

Create a method to avoid vacuous truth?

Added by rydlaw (Waldyr de Souza) over 3 years ago. Updated over 3 years ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:73986]

Description

I often find myself running into unexpected results when using #all? for example

[].all? { |e| false } # => true

Even though it's logically correct could we have a method that express the following?

foo.any? && foo.all?(&:bar)

History

Updated by rydlaw (Waldyr de Souza) over 3 years ago

  • Description updated (diff)

Updated by rydlaw (Waldyr de Souza) over 3 years ago

  • Subject changed from Can create a method to avoid vacuous truth? to Create a method to avoid vacuous truth?

Updated by shevegen (Robert A. Heiler) over 3 years ago

Here is the biggest problem:

  • What name would you give it? :)

Updated by phluid61 (Matthew Kerwin) over 3 years ago

Robert A. Heiler wrote:

Here is the biggest problem:

  • What name would you give it? :)

​I seem to recall someone suggesting 'any_and_all?' in the past. Can't remember if I just made that up, though.

Updated by avit (Andrew Vit) over 3 years ago

Waldyr de Souza wrote:

I often find myself running into unexpected results when using #all? for example

[].all? { |e| false } # => true

If you expect all false, then why not use none?, is it not sufficient?

Even though it's logically correct could we have a method that express the following?

foo.any? && foo.all?(&:bar)

This also has edge cases, try: foo = [nil]

Matthew Kerwin wrote:

I seem to recall someone suggesting 'any_and_all?' in the past.

This makes sense and it's consistent with foo.any?(&block) && foo.all?(&block)

This is still surprising to me, it looks like a contradiction:

[].any? #=> false
[].all? #=> true

I would expect "all" to be a superset of "any": both should mean "at least one".
Is there a reason for the existing behaviour, or is it just history now?

Updated by duerst (Martin Dürst) over 3 years ago

Andrew Vit wrote:

This is still surprising to me, it looks like a contradiction:

[].any? #=> false
[].all? #=> true

I would expect "all" to be a superset of "any": both should mean "at least one".
Is there a reason for the existing behavior, or is it just history now?

It's the way it works in Mathematics. But I'll try to explain it without using too much Mathematical terminology.

all? doesn't ask "at least one"?, but really "are all of those that are in the array true?". If there's no element in the array, then the condition is indeed true for all elements in the array.

Another way to try to understand this is to look at how these operations can be expressed by using reduce:

def any? (&block)
  reduce(q1) { |memo, e| memo or block.call(e) }
end

def all? (&block)
  reduce(q2) { |memo, e| memo and block.call(e) }
end

So both of these operations are map-reduce operations. The mapping is done with the block, converting array elements to true/false booleans. The reduction is 'or' for any? and 'and' for all?. Now the question for you is to figure out what we should put instead of q1 and q2. Hint: First make sure you get q1 and q2 right for arrays with actual elements. Then see what you get from the above definitions with empty arrays.

[In Mathematical terms, false is the neutral element of logical OR, and true is the neutral element of logical AND, the same way 0 is the neutral element of addition, and 1 is the neutral element of multiplication. Therefore, the sum of zero elements is 0, but the product of zero elements is 1.]

Updated by sawa (Tsuyoshi Sawada) over 3 years ago

Martin Dürst wrote:

Andrew Vit wrote:

This is still surprising to me, it looks like a contradiction:

[].any? #=> false
[].all? #=> true

I would expect "all" to be a superset of "any": both should mean "at least one".
Is there a reason for the existing behavior, or is it just history now?

It's the way it works in Mathematics.

In natural language, universal quantification carries a presupposition that the domain is non empty; the meaning of "for all x in A, p(x)" is undefined when A is empty, just like "the king of the United States" is undefined. And whenever universal quantification is defined, the entailment holds: "for all x in A, p(x) → for some x in A, p(x)" (And the word "any" in a question is the same as "some" here). That is probably what Andrew Vit was mentioning. And that is where natural language and formal systems differ, which makes the latter a bit counter-intuitive.

Updated by avit (Andrew Vit) over 3 years ago

Thanks Martin and Tsuyoshi,

Yes, all of this makes sense when you just look at it the right way.
Otherwise this basic equality wouldn't work either:

[].all? { true } == [].none? { false }

I'm not sure a new core method is justified anyway. It's easy to add ourselves
or even create a special implementation of enumerable:

# naive hack (!)
module Enumerable
  def nonempty
    dup.extend NonemptyIterators
  end

  module NonemptyIterators
    def all?
      !empty? && super
    end
    # ...
  end
end

[].all? { true }  # == true
[].nonempty.all? { true } # == false

I'm not sure I would like it to assume the natural language interpretation for me.
It might be enough that ruby core just provides the logic primitives.

Updated by duerst (Martin Dürst) over 3 years ago

Tsuyoshi Sawada wrote:

In natural language, universal quantification carries a presupposition that the domain is non empty; the meaning of "for all x in A, p(x)" is undefined when A is empty, just like "the king of the United States" is undefined. And whenever universal quantification is defined, the entailment holds: "for all x in A, p(x) → for some x in A, p(x)" (And the word "any" in a question is the same as "some" here). That is probably what Andrew Vit was mentioning. And that is where natural language and formal systems differ, which makes the latter a bit counter-intuitive.

I agree that in natural language, expressions that are similar to universal quantification usually assume that the underlying set is not empty. But this isn't so much because that's how natural language works. It is more because natural language is used in context, and it's usually clear from context already that the underlying set is not empty. If it's known that the underlying set is empty, then statements with universal quantifications are simply not made, because they are not needed. This is different from Mathematics and programming, where we as much as possible don't want to treat the empty set as special.

Updated by sawa (Tsuyoshi Sawada) over 3 years ago

Martin Dürst wrote:

But this isn't so much because that's how natural language works.

That is how natural language works. Presupposition (failure) is an essential part of natural language semantics. It is a matter of semantics, not pragmatics. Presupposition and implicature are different.

It is more because natural language is used in context

Natural language exists without context. Sentences can be judged to be syntactically correct or not and can be semantically interpreted irrespective of whether they make sense in context.

If it's known that the underlying set is empty, then statements with universal quantifications are simply not made, because they are not needed.

Lack of spontaneous data is no obstacle for interpreting them semantically. You can construct a sentence in your mind and judge it by introspection.

But what you wrote about formal system is right. I just wanted to clarify Andrew Vit's point.

Updated by nobu (Nobuyoshi Nakada) over 3 years ago

  • Description updated (diff)

Anybody proposed an optional parameter for the default value to #all? and #any? ?

Updated by sawa (Tsuyoshi Sawada) over 3 years ago

Nobuyoshi Nakada wrote:

Anybody proposed an optional parameter for the default value to #all? and #any? ?

any? is not the issue. I don't think Waldyr de Souza wants to alter the behaviour:

[].any?{} # => false

I don't particularly like the original proposal, but if it is going to be adopted in any way, perhaps giving a new method would be appropriate. As pointed out by Martin, this is a case where formal systems and natural languages differ, but there is in fact an example where programming languages adopt concepts from both sides: "or" and "exclusive or". In natural language, when people just say "or", they (usually) mean "exclusive or", but in formal systems, "or" means "(inclusive) or". Since "exclusive or" is sometimes useful in programming, many programming languages do have both. The proposal is similar to this situation. The question is whether the use of foo.any? && foo.all?(&:bar) is frequent enough to be justified as another method.

Also available in: Atom PDF