Jakob Ƙstergaard Hegelund

Tech stuff of all kinds
Archive for September 2016

Useful return from logical or

2016-09-29

This is just one if these little cool things that everyone should know about, but which are too small to really mention. So now I'm going to mention it anyway.

Picking the first useful value

A problem that I run across every now and then is, that I'm given a couple (or more) optional variables (either std::optional, home-brewn Optional or even pointers to values that can either be 0 or actually point to a valid value - conceptually that's the same for the purpose of this post).

std::string printable_customer_name(...) { // Retrieve contact details on customer std::string *fullname = ...; std::string *email = ...; // Now return the fullname if we have it, the e-mail otherwise, // or "-unknown-" if we don't have either if (fullname) return *fullname; if (email) return *email; return "-unknown-"; }

Now this gets the job done. We could have used std::optional instead; there could be all sorts of reasons why we would want that - but actually this would give us access to the .value_or method which seeks to assist us in solving exactly this type of problem. Lo and behold.

std::string printable_customer_name(...) { // Retrieve contact details on customer std::optional fullname = ...; std::optional email = ...; // Now return the fullname if we have it, the e-mail otherwise, // or "-unknown-" if we don't have either return fullname.value_or(email.value_or("-unknown-")); }

While shorter, this is hardly elegant. Imagine having to select between five different values; that's a lot of typing .value_or(). Could we imagine a better way? Well conceptually, we're doing a logical or returning the first value that does not evaluate to logical false. Could we exploit that?

std::string printable_customer_name(...) { // Retrieve contact details on customer std::optional fullname = ...; std::optional email = ...; // Now return the fullname if we have it, the e-mail otherwise, // or "-unknown-" if we don't have either return fullname || email || "-unknown-"; }

Even if the STL had an overloaded operator|| for std::optional and associated trickery to make such a construct possible, this would not generalize to our pointer example earlier. The return value of logical or is a boolean and that's the end of it (at least right now).

But it isn't like that everywhere - and this is what triggered this post. I recently wrote this code in a LISP project for solving this exact problem:

(defun get-printable-name-from (guid) "Returns fullname for account if one exists, otherwise email address, otherwise '-unknown-" (declare (string guid)) (let ((doc (api-get ...))) (car (or (sexp-path doc "contact" "fullname") (sexp-path doc "contact" "email") '("-unknown-")))))

While the above is actual production code, if we re-write it to match the style of our C++ examples, it would look more like:

(defun get-printable-name-from (guid) "..." (let ((fullname ...) (email ...)) (car (or fullname email '("-unknown-")))))

The (or takes the three arguments fullname, email and '("-unknown-"). The two first are variables that either hold the empty list (nil) or a list of one string element. The '("-unknown-") is a list literal, a single element list holding just the string "-unknown-".

What (or does, is, it returns the first element which does not evaluate to false (nil). It does not simply return true or false. This of course has interesting implications when the arguments to or are not of the same type. In other words, it returns the first variable which does not hold the empty list, or, it returns the non-empty list literal. The role of (car is to simply pick the first element of the list that is returned by (or; and we can immediately see that we are guaranteed to always get a non-empty list back from (or.

And that's actually it. It's not much, but having or return the first non-false element instead of simply returning true or false is really convenient.