Limited operator overloading

Operator overloading defines the behavior of certain binary operators for certain types.

struct Vec { x int y int } fn (a Vec) str() string { return '{${a.x}, ${a.y}}' } fn (a Vec) + (b Vec) Vec { return Vec{a.x + b.x, a.y + b.y} } fn (a Vec) - (b Vec) Vec { return Vec{a.x - b.x, a.y - b.y} } fn main() { a := Vec{2, 3} b := Vec{4, 5} mut c := Vec{1, 2} println(a + b) // "{6, 8}" println(a - b) // "{-2, -2}" c += a //^^ autogenerated from + overload println(c) // "{3, 5}" }

Operator overloading goes against V's philosophy of simplicity and predictability. But since scientific and graphical applications are among V's domains, operator overloading is an important feature to have in order to improve readability:

a.add(b).add(c.mul(d)) is a lot less readable than a + b + c * d.

Operator overloading is possible for the following binary operators: +, -, *, **, /, %, <, ==.

Indexing can be overloaded too:

struct Buffer { mut: data []int } fn (b Buffer) [] (index int) int { return b.data[index] } fn (mut b Buffer) []= (index int, value int) { b.data[index] = value }

Custom types can also opt into multidimensional and slice syntax by taking SliceIndex or []SliceIndex:

struct Tensor {} fn (t Tensor) [] (parts []SliceIndex) Tensor { return t } fn main() { t := Tensor{} _ = t[1..3, ..] _ = t[2, 4..8] }

Implicitly generated overloads

  • == is automatically generated by the compiler, but can be overridden.

  • !=, >, <= and >= are automatically generated when == and < are defined. They cannot be explicitly overridden.

  • Assignment operators (*=, **=, +=, /=, etc) are automatically generated when the corresponding operators are defined and the operands are of the same type. They cannot be explicitly overridden.

Restriction

To improve safety and maintainability, operator overloading is limited.

Type restrictions

  • When overriding < and ==, the return type must be strictly bool.
  • Both arguments must have the same type (just like with all operators in V).
  • Overloaded operators have to return the same type as the argument (the exceptions are < and ==).
  • [] and []= overloads are only allowed on structs and type aliases.
  • [] must take exactly one parameter and return a value.
  • []= must take exactly a parameter and a value, use a mut receiver, and return nothing.
  • Use SliceIndex or []SliceIndex for slice syntax like tensor[1..3] or multidimensional syntax like tensor[1, ..].

Other restrictions

  • Arguments cannot be changed inside overloads.
  • Calling other functions inside operator functions is not allowed (planned).