Nested Optionals, `&&`, `||`

In Argentum optional type can wrap any type, including another Optional. This theoretically allows having types like ?int, ??int, ???int, and so on.

Why is this needed? For instance, when accessing a container of bool or optional elements, it's necessary to distinguish between the absence of an element (going out of array bounds) and an element with a value of nothing. Or when using nested ? operators, the result will depend on which condition didn't work.

Let's consider an example:

token = name ? findUserByName(_) ? getUserToken(_);

// Operator associativity is right-to-left. So, this example can be written as:
token = name ? (findUserByName(_) ? getUserToken(_));

Let's go right-to-left:

  • getUserToken returns a value of type ?String, which could be either a token or nothing if the user doesn't have a token.
  • The innermost ? operator will return ??String. It will be nothing if there's no user, some(nothing) if there's no token, or some(some(string)), if there's a token.
  • The left ? operator will have a type ???String, which could be nothing if there's no name, and various forms of some(...) indicating problems with finding the user or their token.

The resulting package of optional values can be analyzed with three ":" operators:

log(token : "No name" : "No user" : "No token)

However, this level of nesting isn't always necessary (and definitely not good for function results). That's why, in Argentum, there's a &&-operator, which works like ?, but it requires both left and right operands to return optional values (not necessarily of the same type).

It functions quite similarly to Haskell's maybe >>=. Its left operand returns ?T, and its right operand transforms T into ?X. The result of this operator is of type ?X.

It starts by executing the left operand:

  • If it's nothing, the result becomes nothing of type ?X.
  • Otherwise, it binds the inner value from ?T to the variable "_" and executes the right operand, which becomes the result of the entire && operator.

Compared to the ? operator, the && operator has only one difference – it doesn't wrap the result of the right operand in an optional; instead, it requires it to be optional already. If you substitute bool (optional void) for ?T and ?X in the &&-operator, it becomes identical to the &&-operator in most C-like languages.

Like the ? operator, && also has the form &&=name, to provide a name instead of "_".

Let's rewrite the previous example:

token = name && findUserByName(_) && getUserToken(_);

Now, the token has a type ?String. It has lost all information about why the token retrieval failed. It's now either "no token" or the actual token value.

Sometimes, nesting of optional wrappers is useful, and sometimes it isn't. That's why both the ? and && operators find their uses.

The last of the unmentioned operators is "||". It's similar to ":". The only difference is that it requires the left and right sides to be of the same optional type. If the ":" operator returns its left operand unwrapped from an optional, "||" doesn't do that. In this aspect, it's also analogous to the || operator in most C-like languages.

Examples of usage:

x = a < 0 || a > 100 ? -1 : 1;

myConfig = getFromFile() || getFromServer() || getDefaultConfig() : terminate();

The last line of code attempts to acquire config from different sources, all "||"-operators in the chain return ?Config which is examined and unwrapped by the last ":"-operator.

Leave a Reply

Your email address will not be published. Required fields are marked *