7

I'm new here. After reading through how to ask and format, I hope this will be an OK question. I'm not very skilled in perl, but it is the programming language what I known most.

I trying apply Perl to real life but I didn't get an great understanding - especially not from my wife. I tell her that:

if she didn't bring to me 3 beers in the evening, that means I got zero (or nothing) beers.

As you probably guessed, without much success. :(

Now factually. From perlop:

Unary "!" performs logical negation, that is, "not".

Languages, what have boolean types (what can have only two "values") is OK:

if it is not the one value -> must be the another one.

so naturally:

!true  -> false
!false -> true

But perl doesn't have boolean variables - have only a truth system, whrere everything is not 0, '0' undef, '' is TRUE. Problem comes, when applying logical negation to an not logical value e.g. numbers.

E.g. If some number IS NOT 3, thats mean it IS ZERO or empty, instead of the real life meaning, where if something is NOT 3, mean it can be anything but 3 (e.g. zero too).

So the next code:

use 5.014;
use Strictures;

my $not_3beers = !3;

say defined($not_3beers) ? "defined, value>$not_3beers<" : "undefined";
say $not_3beers ? "TRUE" : "FALSE";

my $not_4beers = !4;
printf qq{What is not 3 nor 4 mean: They're same value: %d!\n}, $not_3beers if( $not_3beers == $not_4beers );
say qq(What is not 3 nor 4 mean: @{[ $not_3beers ? "some bears" : "no bears" ]}!) if( $not_3beers eq $not_4beers );

say ' $not_3beers>',  $not_3beers, "<";
say '-$not_3beers>', -$not_3beers, "<";
say '+$not_3beers>', -$not_3beers, "<";

prints:

defined, value><
FALSE
What is not 3 nor 4 mean: They're same value: 0!
What is not 3 nor 4 mean: no bears!
 $not_3beers><
-$not_3beers>0<
+$not_3beers>0<

Moreover:

perl -E 'say !!4'

what is not not 4 IS 1, instead of 4!

The above statements with wife are "false" (mean 0) :), but really trying teach my son Perl and he, after a while, asked my wife: why, if something is not 3 mean it is 0 ? .

So the questions are:

  • how to explain this to my son
  • why perl has this design, so why !0 is everytime 1
  • Is here something "behind" what requires than !0 is not any random number, but 0.
  • as I already said, I don't know well other languages - in every language is !3 == 0?
elfiress
  • 73
  • 1
  • 1
  • 4
  • 3
    I feel like there is some profound argument trying to be made here I just fail to see what it is. – Sean Bright Jul 05 '13 at 14:18
  • 1
    going to try the perl's logic with my wife & beers :) – clt60 Jul 05 '13 at 15:25
  • 1
    I've not had not three beers. Does that mean I've had four beers? – TLP Jul 05 '13 at 15:34
  • 1
    Probably your wife using overloaded `!` operator. :) Check the `overload::Overloaded $beer`. – kobame Jul 05 '13 at 16:00
  • 1
    The `!` operator applies to boolean values. The expression `!3` is a shorthand for `!(3 != 0)`, the parenthesized expression representing the implicit boolean value that is being negated. Because `(3 != 0)` is true or 1, its negation is 0 or false. That's why `!3 == !4`. – Jonathan Leffler Jul 05 '13 at 16:01

4 Answers4

12

I think you are focussing to much on negation and too little on what Perl booleans mean.

Historical/Implementation Perspective

What is truth? The detection of a higher voltage that x Volts.

On a higher abstraction level: If this bit here is set.

The abstraction of a sequence of bits can be considered an integer. Is this integer false? Yes, if no bit is set, i.e. the integer is zero.

A hardware-oriented language will likely use this definition of truth, e.g. C, and all C descendants incl Perl.

The negation of 0 could be bitwise negation—all bits are flipped to 1—, or we just set the last bit to 1. The results would usually be decoded as integers -1 and 1 respectively, but the latter is more energy efficient.

Pragmatic Perspective

It is convenient to think of all numbers but zero as true when we deal with counts:

my $wordcount = ...;

if ($wordcount) {
  say "We found $wordcount words";
} else {
  say "There were no words";
}

or

say "The array is empty" unless @array;  # notice scalar context

A pragmatic language like Perl will likely consider zero to be false.

Mathematical Perspective

There is no reason for any number to be false, every number is a well-defined entity. Truth or falseness emerges solely through predicates, expressions which can be true or false. Only this truth value can be negated. E.g.

¬(x ≤ y) where x = 2, y = 3

is false. Many languages which have a strong foundation in maths won't consider anything false but a special false value. In Lisps, '() or nil is usually false, but 0 will usually be true. That is, a value is only true if it is not nil!

In such mathematical languages, !3 == 0 is likely a type error.

Re: Beers

Beers are good. Any number of beers are good, as long as you have one:

my $beers = ...;

if (not $beers) {
  say "Another one!";
} else {
  say "Aaah, this is good.";
}

Boolification of a beer-counting variable just tells us if you have any beers. Consider !! to be a boolification operator:

my $enough_beer = !! $beers;

The boolification doesn't concern itself with the exact amount. But maybe any number ≥ 3 is good. Then:

my $enough_beer = ($beers >= 3);

The negation is not enough beer:

my $not_enough_beer = not($beers >= 3);

or

my $not_enough_beer = not $beers;

fetch_beer() if $not_enough_beer;

Sets

A Perl scalar does not symbolize a whole universe of things. Especially, not 3 is not the set of all entities that are not three. Is the expression 3 a truthy value? Yes. Therefore, not 3 is a falsey value.

The suggested behaviour of 4 == not 3 to be true is likely undesirable: 4 and “all things that are not three” are not equal, the four is just one of many things that are not three. We should write it correctly:

4 != 3    # four is not equal to three

or

not( 4 == 3 )   # the same

It might help to think of ! and not as logical-negation-of, but not as except.

How to teach

It might be worth introducing mathematical predicates: expressions which can be true or false. If we only ever “create” truthness by explicit tests, e.g. length($str) > 0, then your issues don't arise. We can name the results: my $predicate = (1 < 2), but we can decide to never print them out, instead: print $predicate ? "True" : "False". This sidesteps the problem of considering special representations of true or false.

Considering values to be true/false directly would then only be a shortcut, e.g. foo if $x can considered to be a shortcut for

foo if defined $x and length($x) > 0 and $x != 0;

Perl is all about shortcuts.

Teaching these shortcuts, and the various contexts of perl and where they turn up (numeric/string/boolean operators) could be helpful.

  • List Context
    • Even-sized List Context
  • Scalar Context
    • Numeric Context
    • String Context
    • Boolean Context
  • Void Context
amon
  • 57,091
  • 2
  • 89
  • 149
8
  • as I already said, I don't know well other languages - in every language is !3 == 0?
  • Yes. In C (and thus C++), it's the same.

    void main() {
        int i = 3;
        int n = !i;
        int nn = !n;
        printf("!3=%i ; !!3=%i\n", n, nn);
    }
    

    Prints (see http://codepad.org/vOkOWcbU )

    !3=0 ; !!3=1
    

  • how to explain this to my son
  • Very simple. !3 means "opposite of some non-false value, which is of course false". This is called "context" - in a Boolean context imposed by negation operator, "3" is NOT a number, it's a statement of true/false.

    The result is also not a "zero" but merely something that's convenient Perl representation of false - which turns into a zero if used in a numeric context (but an empty string if used in a string context - see the difference between 0 + !3 and !3 . "a")

    The Boolean context is just a special kind of scalar context where no conversion to a string or a number is ever performed. (perldoc perldata)


  • why perl has this design, so why !0 is everytime 1

  • Is here something "behind" what requires than !0 is not any random number, but 0.
  • Nothing aside from simplicity of implementation. It's easier to produce a "1" than a random number.

  • if you're asking a different question of "why is it 1 instead of the original # that was negated to get 0", the answer to that is simple - by the time Perl interpreter gets to negate that zero, it no longer knows/remembers that zero was a result of "!3" as opposed to some other expression that resulted in a value of zero/false.

Community
  • 1
  • 1
DVK
  • 126,886
  • 32
  • 213
  • 327
3

If you want to test that a number is not 3, then use this:

my_variable != 3;

Using the syntax !3, since ! is a boolean operator, first converts 3 into a boolean (even though perl may not have an official boolean type, it still works this way), which, since it is non-zero, means it gets converted to the equivalent of true. Then, !true yields false, which, when converted back to an integer context, gives 0. Continuing with that logic shows how !!3 converts 3 to true, which then is inverted to false, inverted again back to true, and if this value is used in an integer context, gets converted to 1. This is true of most modern programming languages (although maybe not some of the more logic-centered ones), although the exact syntax may vary some depending on the language...

twalberg
  • 59,951
  • 11
  • 89
  • 84
0

Logically negating a false value requires some value be chosen to represent the resulting true value. "1" is as good a choice as any. I would say it is not important which value is returned (or conversely, it is important that you not rely on any particular true value being returned).

chepner
  • 497,756
  • 71
  • 530
  • 681