Saffire update may 2013: coalesce

Warning: This blogpost has been posted over two years ago. That is a long time in development-world! The story here may not be relevant, complete or secure. Code might not be complete or obsoleted, and even my current vision might have (completely) changed on the subject. So please do read further, but use it with caution.
Posted on 22 Apr 2013
Tagged with:

One of the things that happens over and over again is that you need to check a value, and if it’s not set, it should set a default value. Normally, these variables could be initially set by properties, but sometimes you don’t have any control on initialization. For instance, when these values come from users.

In PHP:

if (isset($_POST['variable'])) {
  $a = $_POST['variable'];
} else {
  $a = "default value";
}

or through the ternary operator:

$a = $_POST['variable'] ? $_POST['variable'] : "default";

One of the problems with these constructions, is that when the first operand is true (and thus the default value is not used), the first operand is evaluated twice. As an example:

$a = 0;
function foo() {
  global $a;
  $a++;
  return $a;
}

print foo() ? foo() : "bar"; // 2

This will output “2”. Once foo() is called for the evaluation, the second time foo() is called for actually returning that variable. Since PHP 5.3, the ternary operator can leave out the middle part, in which case it will actually return the first part if this is evaluates to true. This is NOT equivalent to the example above, as this “shortsyntax” actually evaluates once:

$a = 0;
function foo() {
  global $a;
  $a++;
  return $a;
}

print foo() ?: "bar"; // 1

In the last few days, Kevin (@ikke) has implemented this feature through the coalesce operator which can be used like this in Saffire:

import io;

val = "";
io.print( val ?? "default", "\n");

Again, just like PHP shortcut ternary operator, the coalesce operator will use the first value if this value evaluates to boolean “true”. Internally, it will try and implicitly cast this through the __boolean() method on the object given. If this evaluates to false, it will return the value right after the ??. Both the left side and the right side of the ?? can be expressions.

It’s even possible to “chain” so we can coalesce multiple values, ie: we can find the first non-false value easily:

io.print( (user.firstname() ?? user.lastname() ?? user.email() ?? "unknown user"), "\n");

Note that currently you need to add () if you chain multiple coalesce operators. In this example, we print either the users firstname, lastname, email address or just “unknown user”, which ever is defined first.

Because coalesce internally is structured differently than simple if-else or a ternary operation, it’s much more optimal to use coalesce.