Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The puzzle is referring to the concept of a vacuous truth (https://en.wikipedia.org/wiki/Vacuous_truth).

In most logic frameworks, the All function (upside down A in standard logic notation) is true if and only if no statement within the set is false (i.e. All his hats are green if he has no hats). This is for several reasons:

- it allows for more coherent empty set functions. For example if we take the power set of a set, that power set has the same All value as the standard set (since the power set includes the empty set)

- it allows for early stopping on false statements. So you can define the statement as a lazy executor of all child conditions



I learned about Vacuous Truth the hard way recently when I found out that `every` method in JavaScript returns `true` for empty arrays as well.


In programing, you can always rewrite that first rule as "all" and "some" must compose over set union. So, "all (A ∪ B) == all A && all B", and "some (A ∪ B) == some A || some B".

That lets you discover the answer for the empty set.


Which leads to a funny fact that if all elements of the set S satisfy proposition P it doesn’t necessarily imply that some elements of the set S satisfy proposition P.


My description of the power set is by definition allowing all to imply some


I don’t follow. Can you elaborate?


The power set of a set S, P(S) (or sometimes 2^S) is the set of all subsets of S including both the empty set and the set itself.

To bridge the gap with programming, make a map f: S -> bool which represents our predicate.

all(f, S) => either S is empty or for all elements s in S, f(s) = True.

Now make f work on sets as well as individual values. f({x, y}) means True if f(x) and f(y) are True, False otherwise.

all(f, S) => all(f, P(S))

If we take the opposite and define all(f, {}) = False then this doesn't work and in addition all(f, P(S)) = False for all sets S.


That doesn’t explain how all(f,{}) => some(f,{}) may be made true with your definition preserving the distributive property.


As I think you would hope from a practical standpoint -- you don't want to have to handle a special case of false and always check if the array is empty.

I agree it's only logical in engineering contexts like that though, not in everyday language.


This is even what I expected, but I majored in math, so maybe that biased my response.


But if you consider “liar” to be an object,

> liar.hats.every((hat) => hat.color === "green")

will throw a TypeError: Cannot read properties of undefined. That’s definitely not `true`.


This depends on `liar.hats` being undefined, but what if `liar.hats` is an empty array? That seems like an equally valid way of representing a person that owns no hats.


You implemented the problem wrong, and thus got an error. If hats is a list of colors, then every hat != green is true if the list is empty.


I wouldn’t say it is wrong per se. It certainly defies the conventional translation into FOL, but there is no a priori reason to pick the conventional formalism of FOL for this problem.


I don't think that's true. What if the liar buys a red hat?

    liar.hats.push("red")
This only works if hats is an empty array. If hats is just not a property people have (undefined in the example), then you can't represent adding them.

Now you might argue hats can be null when a user doesn't have them, or a non-empty array, but that's clearly not a great way to represent that. Now you have owning no hats represented two different ways as an empty array or null, and must build special casing around the null case (unless you are using a language where nil and the empty array are one and the same)


Hm. No.

    const liar = { hats: [] };
    liar.hats.every(hat => hat.color === "green")

    true


As a programmer, my favorite way to think about Vacuous Truth is to think about the relationship between binary operators and their corresponding sequence operators. Think about taking an operator like "plus" and extending it to sequences or lists of things. What properties would we like those extensions to have? Let's start with a simple example:

Sum is the "sequence extension" of Plus.

    Sum([a, b, c]) = a + b + c -- basically our definition

    Sum([a, b, c]) = Sum([a, b]) + Sum([c]) -- distributive

    Sum([]) = 0 -- identity
Let's keep things simple by assuming our operator is associative and has an identity (i.e., it is a monoid). We can take any monoid and extend it to sequences. Assume @ is some monoid. We define the sequence aggregate "Agg" as:

    [0] Agg([a, b, c]) = a @ b @ c == ((a @ b) @ c) == (a @ (b @ c)) -- definition + associativity

    [1] Agg([a, b, c]) = Agg([a, b]) @ Agg([c]) -- distributive

    [2] Agg([]) = Identity(@)
Note that property 2 is required if we want Agg([]) to have a value at all, since Agg([]) == Agg([] concat []) == Agg([]) @ Agg([]). If Agg([]) doesn't have a value, then it's not really properly distributive, since Agg([a]) == Agg([a] concat []) == Agg([a]) @ Agg([]) == ???. So we see that if we have an identity for the operator, it really should be the same as Agg([]).

So let's extend AND and OR to sequence operators. The extension of AND can be called "Every", and it operates on sets of booleans. In particular, Every([]) == Identity(AND). The Identity of AND is "true", so Every([]) == True. The extension of OR can be called "Any", and Any([]) == Identity(OR) == False.

This is the easiest way for me to remember the truth values of Every([]) and Any([]): they must be the identities of the corresponding boolean operators.

Any([Your name is Bob, you can fly]) == Any([Your name is Bob, you can fly] concat []) == Any([Your name is Bob]) OR Any([You can fly]) OR Any([]) == (Your name is Bob) OR (You can fly) OR (False). Any([]) == False.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: