Open or Die

Every programming language I can think of does an optimization where boolean operators determine flow. It seems like a good idea at first, but it's also inconsistent. Here's the phenomenon I'm taking about starting with C:

C

#include <stdio.h>

int foo()
{
    printf("foo\n");
    return 1;
}

int bar()
{
    printf("bar\n");
    return 1;
}

int main(int argc, char** args)
{
    printf("%d\n", foo() || bar());
    return 0;
}

Output:

foo
1

The question is, why doesn't it print this?:

foo
bar
1

And the answer is: bar() didn't run, because C said to itself:

foo() || bar() that's two things or-ed together, the first thing is true, which means, no matter what the second thing is, the expression is true, so we don't have to evauate it.

If you've never seen this before, you might be forgiven for thinking it's a C thing. But weirdly, it seems fairly universal.

Python

def foo():
    print "foo"
    return True

def bar():
    print "bar"
    return True

print(foo() or bar())

Ouput:

foo
True

Lua

function foo()
    print("foo")
    return true
end

function bar()
    print("bar")
    return true
end

print(foo() or bar())

Output:

foo
true

JavaScript

function foo()
{
    console.log("foo")
    return true
}

function bar()
{
    console.log("bar")
    return true
}

console.log(foo() || bar())

Output:

foo
true

Java

class MyFooBar
{
    public static boolean foo()
    {
        System.out.println("foo");
        return true;
    }

    public static boolean bar()
    {
        System.out.println("bar");
        return true;
    }

    public static void main(String args[])
    {
        System.out.println(foo() || bar());
    }
}

Output:

foo
true

Rust

fn foo() -> bool
{
    println!("foo");
    true
}

fn bar() -> bool
{
    println!("bar");
    true
}

fn main()
{
    println!("{}", foo() || bar());
}

Output:

foo
true

Ruby

def foo
    print "foo\n"
    true
end

def bar
    print "bar\n"
    true
end

print (foo() or bar())

Output:

foo
true

Perl

sub foo {
    print "foo";
    return 1
}

sub bar {
    print "bar";
    return 1
}

print (foo or bar)

Output:

foo1

It's kinda remarkable to me that all those languages made the same call, especially when they disagree about so many other things. I guess I can see why, I mean, it comes in handy. In Perl, you often see this:

open(my $fh, "<", "somefile.txt") or die "error!";

So, it kinda says "open or die", which is whimsical, and maybe reads naturally to English-speakers.

In JavaScript and Lua you'll sometimes see a similar trick for getting a default value when a variable is undefined, like in JavaScript, you might see something like this:

function resize(height, width)
{
    height = height || 600;
    width = width || 800;
    // ...
}

In Lua, there's a convention of using and and or to hand-roll a ternary operator, so you'll see this:

local width = isBig() and 300 or 30

instead of:

local width
if isBig() then
    width = 300
else
    width = 30
end

It's just one line, so that's better, right?

Eh... I don't know about all this. It's neat how these short expressions read, but the underlying optimization actually represents a tiny inconsistency in the language. It's inconsistent because "and" and "or" are binary operators, but they have something that no other binary operators have; they control flow. With other binary operators...

foo() + bar()
foo() - bar()
foo() * bar()
foo() / bar()
foo() | bar()
foo() & bar()
foo() == bar()
foo() != bar()

...you can depend on foo() and bar() to both execute. But not with boolean operators? Huh?

Consider this C code:

#include <stdio.h>

int foo()
{
    printf("foo\n");
    return 0;
}

int bar()
{
    printf("bar\n");
    return 0;
}

int main(int argc, char** args)
{
    printf("%d\n", foo() * bar());
    return 0;
}

Output:

foo
bar
0

Even though a similar optimization could work here. Remember, the rationale with this:

foo() or bar()

If foo() is true, we needn't bother running bar() because we know the value of the whole expression already.

That's also the case here when foo() is 0:

foo() * bar()

and yet, the language does not cull the call to bar().

I don't like it. To me, it would be cleaner if I could think about boolean operators as two-argument functions. Instead, they are not like functions, the're actually more like if-statements, because they determine what code executes.

What if we made a language without this exceptional behavior? What if "and" and "or" were just functions? Come on, let's dream big!