Language Design: Unary Operators are Unnecessary
TL;DR: Use methods.
Many languages provide unary operators, usually written as a prefix to the value they apply to.
The most common unary operators are:
!
: Logical complement (on booleans)~
: Bitwise complement (on numbers)-
: Numeric complement (on numbers)+
: useless (on numbers)
Except for reasons of tradition and familiarity, their privileged position in many languages is unnecessary.
They provide rather limited benefits – while adding complexity to the core language.
Unary operators are a waste of a language’s complexity budget.
An alternative is to define methods on the respective types, dropping unary operators altogether:
not
replaces!
on booleans:someBool.not
instead of!someBool
not
replaces~
on integers:1.not
instead of~1
negate
replaces-
on numbers:1.negate
instead of-1
This also elegantly solves the question whether
let x = 1
-x.abs
should evaluate to 1
or -1
, as
x.negate.abs
is completely unambiguous.
There are two additional benefits to the use methods instead of operators:
-
Using methods instead of unary operators moves the negation closer to the thing being negated:
if language.users.map(_.lastName).contains("Smith").not then ... else ...
. The more traditionalif !language.users.map(_.lastName).contains("Smith") then ... else ...
requires users to read the negation first, then read the condition to the end of the line to figure out what is being negated.1 -
Using methods instead of unary operators allows the use of more elaborate result types: While a
negate
operation may always succeed on arbitrary-precision numbers (BigInt
,BigDec
, …), the same operation on the more common fixed-size types (Int
,Long
, …) could benefit from returning an optional result to indicate that a negative value may lack a positive counterpart.
Appendix
Incomplete list of languages and their interpretation of -1.abs
:
-1.abs | let x = 1; -x.abs | |
---|---|---|
C# | -1 | -1 |
D | -1 | -1 |
Dart | -1 | -1 |
Fantom | -1 | -1 |
Groovy | -1 | -1 |
Kitten | 1 | n.a. |
JavaScript | -1 | -1 |
Nim | -1 | -1 |
Raku | -1 | -1 |
Ruby | 1 | -1 |
Rust | -1 | -1 |
Scala | 1 | -1 |
Smalltalk | 1 | n.a. |
-
The Rust community had a similar discussion about this topic. ↩