We had a discussion in the OTP team about what the matching operator should be. In the previous OTP Technical Board meeting, we decided that we should not overload the <- operator used in list comprehensions. Eliminating operators that could be useful for other purposes in the future, we ended up with <~ earlier this week. Unfortunately, <- and <~ look quite similar to each other (especially in some fonts and text sizes), so we wasn’t completely happy with that choice.
After some more discussion and thinking, we came up with ?=. Now that may seem to cause ambiguity since macro invocations start with ?, but it is not. Even if there exists a macro named =, it must be invoked like ?'='.
Thus, the previous example will now look like:
commit_write(OpaqueData) ->
maybe
ok ?= disk_log:sync(OpaqueData#backup.file_desc),
ok ?= disk_log:close(OpaqueData#backup.file_desc),
ok ?= file:rename(OpaqueData#backup.tmp_file, OpaqueData#backup.file),
{ok, OpaqueData#backup.file}
end.
We will have an OTP Technical Board meeting soon to discuss this latest revision of the EEP and approve it (I hope). We will update the EEP after the meeting.
I hadn’t seen that EEP before, it looks great! I usually end up implementing my own short-circuting higher-level sequencing function in every other project I do, and this will definitely be a cleaner way.
?= exists as an operator in groovy, as the “Elvis assignment operator”, and has a similar(ish) purpose, general handling of “unhappy path” values (or setting of default values). It assigns the value on the right hand side to the variable on the left hand side, if and only if the left hand side is falsy (null, empty string, etc). errorMessage ?= "Unknown error"
Maybe not the most popular language, but still the familiarity and similarity in function is an added bonus to those familiar with it!
Whilst I don’t have the full context of the EEP and its history, I prefer this operator as the “?” part of it seems a natural fit with the keyword “maybe”.
I’ve seen that EEP and it looks awesome! I do quite like ?= after looking at it for a couple of minutes, it aligns better with what = is doing but with a possibly conditional early-out.
I recall Joe saying to me that if you add something to a language, you should also take something out (roughly paraphrased). So what should be removed to make space for this?
Parameterized modules was an experimental feature that we had in the language for several releases. The feature mostly worked but was not fully supported by the rest of the OTP (I don’t remember exactly, but it could have been release handling). Eventually, we decided that we would either have to fully support parameterized modules or take them out. We took them out.
Tuple calls (needed to implement parameterized modules) remained for a while until we removed them too.
I can think of one supported feature of the language that we have actually managed to remove. It is another kind of tuple call that was frequently used before funs were invented:
Maybe it is too late for this comment, but the reason why reusing <- makes sense to me is because both maybe and comprehensions are monadic operations where <- have the same semantic meaning. I understand that Erlang does not have monads, and most users won’t be aware of such background, but I wanted to mention there is a framework to describe it.
I’m in fully agreement with Jose on this one and also hopefully not too late to weigh in… I’d favor <- unless it significantly complicates the underlying implementation. I don’t think anyone will be confused.
I was less a fan of parameterized modules, but tuple calls allows for a lot of usually higher-typed functionality (like think OCaml’s first class modules), which is very difficult to emulate otherwise (have to go back down to carrying a load of data everywhere you go).
We have now had the OTP Technical Board meeting. While we spent some time discussing monads , we still decided to go with the ?= operator, since there is not really any cost to adding a new operator and overloading an existing operator will not make it any easier to document it, or for a new user to understand it.
I will update the EEP, and I will continue to implement the maybe construct in the rest of OTP (shell, tools, documentation, and so on).
?= can have a fun case with macros where you do something like:
-define(OP, =).
f(X) ->
maybe
X ??OP exp()
end
There’s no real practical case for it, but in doing so the X ?= exp() expansion never takes place and instead we get X "=" exp() as a result. That’s an edge case and not something I would expect to seriously be an issue.
?? is a macro operator, but only valid inside the definition of a macro like this:
-define(STRINGIZE(Arg), ??Arg).
Defined like this, the ?STRINGIZE() macro will turn its argument into a string.
Even if ?? did not have a special meaning, it would still not work, because the preprocessor never combines adjacent tokens into a new token. That is, the following example:
-define(EQ, =).
-define(LT, <).
t(X, Y) ->
X ?EQ?LT Y.
will be expanded to:
t(X, Y) ->
X = < Y.
which is a syntax error:
t.erl:8:11: syntax error before: '<'
% 8| X ?EQ?LT Y.
% | ^