Repository: go
Issue: proposal: spec: operator functions
Labels: LanguageChange,Proposal,LanguageChangeReview

The only issue that I could _find_ about operator overloading currently #19770, although it's currently closed and doesn't have many details. 

# Goal

Operator overloading should be used to create datatypes that represent things that already exist in Go. They should not represent anything else, and should ideally have no other function.

**New operators should not be allowed to defined**, we should only be able to define currently existing operators. If you look at languages that let you define your own operators (Scala, looking at you!) the code becomes extremely messy and hard to read. It almost _requires_ an IDE because operator overloading is very heavily used.

**Using an operator for anything _other_ than it's purpose is a bad idea**. We should not be making data structures with fancy looking `+` operators to add things to the structure. Overloading the `+` operator in Go should _only_ be used for defining addition/concatenation operators for non-builtin types. Also, as per Go spec, binary operators should _only_ be used for operating on two values of the same type.

**Operators should only operate on and return a single type**. This keeps things consistent with how operators currently work in Go. We shouldn't allow any `type1 + string -> type1` stuff.

**Operators should only be defined in the same package as the type they are defined on**. Same rule as methods. You can't define methods for structs outside your package, and you shouldn't be able to do this with operators either.

And last but not least, **operators should never mutate their operands**. This should be a contract that should be listed in the Go spec. This makes operator functions predictable, which is how they should be.

Unary operators should not need to be overloaded.
```
+x                          is 0 + x
-x    negation              is 0 - x
^x    bitwise complement    is m ^ x  with m = "all bits set to 1" for unsigned x
                                      and  m = -1 for signed x
```
This part of the spec should always remain true, and should also remain true for anything using these operators. Perhaps `^x` may need to have it's own, as there's no good way to define "all bits set to 1" for an arbitrary type, although defining a `.Invert()` function is no less readable IMO.

Unary operations on structs would then therefore be `Type{} + t` or `Type{} - t`, and pointers would be `nil + t` and `nil - t`. These may have to be special cases in the implementation of operator functions on pointers to types.

Assignment operators should also never be overloaded.
```
An assignment operation x op= y where op is a binary arithmetic operator is equivalent to x = x op (y) but evaluates x only once. The op= construct is a single token.
```
This should remain the same just as unary operators.

If we do not permit overloading the `^x` unary operator, this means that we only need to define binary operations.

# Issues/Projects aided by operator overloading
#19787 - Decimal floating point (IEEE 754-2008)  
#26699 - Same proposal, more detail  
#19623 - Changing `int` to be arbitrary precision
#9455 - Adding `int128` and `uint128`
[this code](https://play.golang.org/p/oSPl1uRxd8U) - Seriously it's gross  
[really anything that uses math/big that isn't micro-optimized](https://github.com/deanveloper/modmath/blob/master/v1/bigmod/modmath.go)
If I went searching for longer, there'd probably be a few more that pop up

# Syntax

What's a proposal without proposed syntaxes?
```
// A modular integer.
type Int struct {
	Val int
	Mod int
}

// ==============
// each of the functions would have the following function body:
//
//    	if a == Int{} { // handle unary +
//		a.Mod = b.Mod
//	}
//
//	checkMod(a, b)
//	nextInt = Int{Val: a.Val + b.Val, Mod: a.Mod}
//	nextInt.Reduce()
//
//	return nextInt
//
// ==============

// In all of these, it would result in a compile error if the types of the arguments
// do not match each other and the return type.

// My new favorite. This makes for a simple grammar. It allows
// people who prefer function calls can instead use the `Add` function.
operator (Int + Int) Add
func Add(a, b Int) Int { ... }

// My old favorite. Abandoning the `func` construct clarifies
// that these should not be used like a standard function, and is much
// more clear that all arguments and the return type must be equal.
op(Int) (a + b) { ... }
operator(Int) (a + b) { ... }      // <- I like this better

// My old second favorite, although this looks a lot like a standard method definition.
// Maybe a good thing?
func (a + b Int) Int { ... }

// It can be fixed with adding an "op" to signify it's an operator function, although
// I do not like it because it just reads wrong. Also, looks like we're defining a package-level
// function named `op`... which is not what we are doing.
func op (a + b Int) Int { ... }

// Although at the same time, I don't like having words
// before the `func`... I feel that all function declarations should begin with `func`
op func (a + b Int) Int { ... }

// Another idea could just be to define a method named "Plus", although this
// would cause confusion between functions like `big.Int.Plus` vs `big.Int.Add`.
// We probably need to preserve `big.Int.Add` for microoptimization purposes.
func (a Int) Plus(b Int) Int { ... }
```

# Considering other languages' implementations.

### C++
```
// there's several ways to declare, but we'll use this one
Type operator+(Type a, Type b)
```
I think C++ isn't a bad language, but there are a lot of new programmers who use it and think it's "super cool" to implement operator functions for everything they make, including stuff like overloading the `=` operator (which I have seen before).

I also have a couple friends from college who really enjoyed defining operator functions for _everything_... no bueno.

It gives too much power to the programmer to do everything that they want to do. Doing this creates messy code.

### Swift
```
static func +(a: Type, b: Type) -> Type
```
Note that custom operators may be defined, and you can define stuff like the operator's precedence. I have not looked much into how these operators end up being used, though.

### C#
```
public static Type operator+ (Type a, Type b)
```
Operator functions in C# end up being massively overused in my experience. People define all of the operators for all of their data structures. Might just be a consequence of using a language with many features, though.

### Kotlin
```
operator fun plus(b: Type): Type // use "this" for left hand side
```
https://kotlinlang.org/docs/reference/operator-overloading.html, actually a really nice read.

Operator functions get used _everywhere_, and even the standard library is [_littered_](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/index.html) with them. Using them leads to unreadable code. For instance, what does `mutableList + elem` mean? Does it mutate `mutableList`? Does it return a new `MutableList` instance? No one knows without looking at the documentation.

Also, defining it as a method instead of a separate function just _begs_ it to mutate `this`. We do not want to encourage this.

# Open Questions

### Which operators should be allowed to be overridden?

So, the mathematical operators `+`, `-`, `*`, `/`, `%` are pretty clear that if this gets implemented, we'd want to overload these operators.

List of remaining operators that should be considered for overloading:
 * `<<` and `>>` (I do not think this is a good idea)
 * `|`, `&`, and `&^` (also do not think this is a good idea)
 * `<`, `>`, `<=`, `>=`, `==`, `!=` (maybe a good idea?)
   * If we include `<`, we should include `==` to prevent the confusing case of `x <= y && y >= x` but `x != y`.

Overloading equality _may_ be a good thing. `big.Int` suffers because the only way to test equality is with `a.Cmp(b) == 0` which is not readable _at all_.

I have left out `||` and `&&` because they should be reserved exclusively for `bool` or types based on `bool` (has anyone ever even based a type on `bool`?) and see no reason to override them.

### Should we even allow operator overloading on pointer types?

Allowing operator overloading on a pointer type means the possibility of mutating, which we do not want. On the other hand, allowing pointer types means less copying, especially for large structures such as matrices. This question would be resolved if the [read only types](https://github.com/golang/go/issues/22876) proposal is accepted.

##### Disallowing pointer types
* Does not allow mutation
* No need for `nil` checks in operator implementation

##### Allowing pointer types
* Allows operators to be consistent with the rest of the type's methods.
  * ie `*big.Int` is `*big.Int` everywhere else, it would be good for consistiency
* Since it's consistent, it makes it easier to pass into other functions.
  * ie Can't pass `big.Int` into a function that takes `*big.Int`

Perhaps it should be a compile-time error to mutate a pointer in an operator function. If read-only types were added then we could require the parameters to be read-only.

### Should it reference/dereference as needed?

Methods currently do this with their receivers. For instance:
```
// Because the receiver for `NewInt` is `*big.Int`,
// the second line is equivalent to `(&num).NewInt(....)`

var num big.Int
num.NewInt(5000000000000000000)
```
So should the same logic apply to operator functions?

-----

I'm aware that this will probably not be added to Go 2, but I figured it would be a good thing to make an issue for, since the current issue for Operator Functions is quite small and, well, it's closed.

-----
Edits:
* Added #9455 to list of proposals which could be solved using this instead
* Fixed a clarity issue
* Changed examples to use a modular integer rather than a `*big.Int` since the `math/big` package is designed to be used in an efficient way, and added that read-only types would benefit this proposal