Overloaded operators

From Rosetta Code
Overloaded operators is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

An overloaded operator can be used on more than one data type, or represents a different action depending on the context. For example, if your language lets you use "+" for adding numbers and concatenating strings, then one would say that the "+" operator is overloaded.

Task

Demonstrate overloaded operators in your language, by showing the different types of data they can or cannot operate on, and the results of each operation.



6502 Assembly

Many commands have multiple addressing modes, which alter the way a command is executed. On the 6502 most of these are in fact different opcodes, using the same mnemonic. <lang 6502asm>LDA #$80 ;load the value 0x80 (decimal 128) into the accumulator. LDA $80 ;load the value stored at zero page memory address $80 LDA $2080 ;load the value stored at absolute memory address $2080. LDA $80,x ;load the value stored at memory address ($80+x). LDA ($80,x) ;use the values stored at $80+x and $81+x as a 16-bit memory address to load from. LDA ($80),y ;use the values stored at $80 and $81 as a 16-bit memory address to load from. Load from that address + y.</lang>

ALGOL 68

This overrides the standard integer + operator (as in the F# sample) and provides an overloaded TOSTRING operator. Also, the + operator is overloaded to operate on an INT left-hand operand and a BOOL right-hand operand. <lang algol68>BEGIN

   # Algol 68 allows operator overloading, both of existing operators and new ones  #
   # Programmer defined operators can be a "bold word" (uppercase word) or a symbol #
   # Symbolic operators can be one or two characters, optionally followed by := or  #
   # =:, =: can also be defined as an operator (Allowed in Algol 68G, possibly not  #
   # in other implementations)                                                      #
   # the characters allowed in a symbolic operator depends on the implementation    #
   # but would include +, -, *, /, <, =, >                                          #
   # define a new TOSTRING operator and overload it #
   OP TOSTRING = ( INT  n )STRING: whole( n, 0 ); # returns a string representation of n in the minimum width #
   OP TOSTRING = ( BOOL b )STRING: IF b THEN "true" ELSE "false" FI;
   # overide a standard operator #
   INT a = 10, b = 11, c = 21;
   BEGIN
       OP + = ( INT a, INT b )INT: a - b;
       # + between strings is a standard operator that does string concation #
       print( ( TOSTRING a + " ""+"" " + TOSTRING b + " = " + TOSTRING ( a + b ) + " = " + TOSTRING c + "? " + TOSTRING ( ( a + b ) = c )
              , newline
              )
            )
   END;
   # same print, with the stndard + #
   print( ( TOSTRING a + "  +  " + TOSTRING b + " = " + TOSTRING ( a + b ) + " = " + TOSTRING c + "? " + TOSTRING ( ( a + b ) = c )
          , newline
          )
        );
   # overload + to allow a BOOL to be added to an INT #
   OP + = ( INT a, BOOL b )INT: IF b THEN a + 1 ELSE a FI;
   print( ( TOSTRING a + " ""+"" " + TOSTRING ( a = 10 ) + " = " + TOSTRING ( a + ( a = 10 ) ), newline ) )

END</lang>

Output:
10 "+" 11 = -1 = 21? false
10  +  11 = 21 = 21? true
10 "+" true = 11

F#

For those who complain that they can't follow my F# examples perhaps if I do the following it will help them. <lang fsharp> // Overloaded operators. Nigel Galloway: September 16th., 2021 let (+) (n:int) (g:int) = n-g printfn "%d" (23+7) </lang>

Output:
16

Nim

Nim allows overloading of operators. There is no restrictions regarding types of arguments when overloading an operator. For instance, we may define a vector type and addition of vectors:

<lang Nim>type Vector = tuple[x, y, z: float]

func `+`(a, b: Vector): Vector = (a.x + b.x, a.y + b.y, a.z + b.z)

echo (1.0, 2.0, 3.0) + (4.0, 5.0, 6.0) # print (x: 5.0, y: 7.0, z: 9.0)</lang>

The list of predefined operators with their precedence can be found here: https://rosettacode.org/wiki/Operator_precedence#Nim

Nim allows also user defined operators which must be composed using the following characters:

= + - * / < > @ $ ~ &  % | !  ? ^ .  : \

For instance, we may define an operator ^^ the following way:

<lang Nim>func `^^`(a, b: int): int = a * a + b * b</lang>

To determine the precedence of user-defined operators, Nim defines a set of rules:

Unary operators always bind stronger than any binary operator: $a + b is ($a) + b and not $(a + b).

If an unary operator's first character is @ it is a sigil-like operator which binds stronger than a primarySuffix: @x.abc is parsed as (@x).abc whereas $x.abc is parsed as $(x.abc).

For binary operators that are not keywords, the precedence is determined by the following rules:

Operators ending in either ->, ~> or => are called arrow like, and have the lowest precedence of all operators.

If the operator ends with = and its first character is none of <, >, !, =, ~, ?, it is an assignment operator which has the second-lowest precedence.

Otherwise, precedence is determined by the first character.

Phix

Phix does not allow operator overloading and it is not possible to define new operators.
(Fairly weak arguments for, pretty strong against, any few minutes saved typing something in the first time are almost always lost the first time it needs to be maintained, if you want my opinion)

The standard arithmetic operators accept (mixed) integer and floating point values without casting.

The relational operators accept integer, float, string, and sequence values.

The logical operators only accept atoms, however there are 40-something sq_xxx() builtins that can be used
to perform all the builtin operations on any mix of integer, float, string, or sequence values.

Subscripts and concatenation work equivalently on strings and sequences, and in fact concatenation on integers and floats.

Any parameter can be integer, float, string, or sequence if it is declared as an object.

For example printf() can accept [a file number, format string and] a single atom or a sequence of objects,
it being wise to wrap lone strings in {} to ensure you get the whole thing not just the first letter.

Inline assembly mnemonics have multiple implicit addressing modes as per the standard intel syntax.

printf(1,"%g\n",3.5 + 3)                -- 6.5
printf(1,"%t\n",3.5 > 3)                -- true
printf(1,"%t\n","a" = "a")              -- true
printf(1,"%t\n",{1} = {2})              -- false
printf(1,"%V\n",{{1} & {2}})            -- {1,2}
printf(1,"%V\n",{1 & 2.3})              -- {1,2.3}
printf(1,"%V\n",{"a" & "b"})            -- "ab"
printf(1,"%V\n",{"AB"[2] & {1,2}[1]})   -- {66,1}

integer i
#ilASM{ lea eax,[i]
        mov [eax],ebx
        mov [i],ebx
        mov [i],0
        mov eax,ebx
        mov eax,1   -- etc
      } 

Raku

While it is very easy to overload operators in Raku, it isn't really common... at least, not in the traditional sense. Or it's extremely common... It depends on how you view it.

One of the founding principles of Raku is that: "Different things should look different". It follows that "Similar things should look similar".

To pick out one tiny example: Adding numbery things together shouldn't be easily confusable with concatenating strings. Instead, Raku has the "concatenation" operator: ~ for joining stringy things together.

Using a numeric-ish operator implies that you want a numeric-ish answer... so Raku will try very hard to give you what you ask for, no matter what operands you pass it.

Raku operators have multiple candidates to try to fulfil your request and will try to coerce the operands to a sensible value.

Addition: <lang perl6>say 3 + 5; # Int plus Int say 3.0 + 0.5e1; # Rat plus Num say '3' + 5; # Str plus Int say 3 + '5'; # Int plus Str say '3' + '5'; # Str plus Str say '3.0' + '0.5e1'; # Str plus Str say (2, 3, 4) + [5, 6]; # List plus Array</lang>

+ is a numeric operator so every thing is evaluated numerically if possible

Output:
8
8
8
8
8
8
5 # a list or array evaluated numerically returns the number of elements


Concatenation: <lang perl6>say 3 ~ 5; # Int concatenate Int say 3.0 ~ 0.5e1; # Rat concatenate Num say '3' ~ 5; # Str concatenate Int say 3 ~ '5'; # Int concatenate Str say '3' ~ '5'; # Str concatenate Str say '3.0' ~ '0.5e1'; # Str concatenate Str say (2, 3, 4) ~ [5, 6]; # List concatenate Array</lang>

~ is a Stringy operator so everything is evaluated as a string (numerics are evaluated numerically then coerced to a string).

Output:
35
35
35
35
35
3.00.5e1
2 3 45 6 # default stringification, then concatenate

There is nothing preventing you from overloading or overriding existing operators. Raku firmly believes in not putting pointless restrictions on what you can and can not do. Why make it hard to do the "wrong" thing when we make it so easy to do it right?

There is no real impetus to "overload" existing operators to do different things, it is very easy to add new operators in Raku, and nearly any Unicode character or combination may used to define it. They may be infix, prefix, postfix, (or post-circumfix!) The precedence, associativity and arity are all easily defined. An operator at heart is just a subroutine with funny calling conventions.

Borrowed from the Nimber arithmetic task:

New operators, defined in place. Arity is two (almost all infix operators take two arguments), precedence is set equivalent to similar existing operators, default (right) associativity. The second, ⊗, actually uses itself to define itself.

<lang perl6>sub infix:<⊕> (Int $x, Int $y) is equiv(&infix:<+>) { $x +^ $y }

sub infix:<⊗> (Int $x, Int $y) is equiv(&infix:<×>) {

   return $x × $y if so $x|$y < 2;
   my $h = exp $x.lsb, 2;
   return $h ⊗ $y ⊕ (($x ⊕ $h) ⊗ $y) if $x > $h;
   return $y ⊗ $x if $y.lsb < $y.msb;
   return $x × $y unless my $comp = $x.lsb +& $y.lsb;
   $h = exp $comp.lsb, 2;
   (($x +> $h) ⊗ ($y +> $h)) ⊗ (3 +< ($h - 1))

}

say 123 ⊗ 456;</lang>

Output:
31562


That's all well and good, but suppose you have a new class, say, a Line class, and you want to be able to do arithmetic on Lines. No need to override the built in arithmetic operators, just add a multi candidate to do the right thing. A multi allows adding a new definition of the operator without disturbing the existing ones.

Very, very basic Line class: <lang perl6>class Line {

   has @.start;
   has @.end;

}

  1. New infix + multi to add two Lines together, for some bogus definition of add

multi infix:<+> (Line $x, Line $y) {

   Line.new(
      :start(
         sqrt($x.start[0]² + $y.start[0]²),
         sqrt($x.start[1]² + $y.start[1]²)
      ),
      :end(
         sqrt($x.end[0]² + $y.end[0]²),
         sqrt($x.end[1]² + $y.end[1]²)
      )
   )

}

  1. In operation:

say Line.new(:start(-4,7), :end(5,0)) + Line.new(:start(1,1), :end(2,3));</lang>

Output:
Line.new(start => [4.123105625617661e0, 7.0710678118654755e0], end => [5.385164807134504e0, 3e0])

To be fair, all of this easy power in a bad programmers hands can lead to incomprehensible code... but bad programmers can be bad in any language.

REXX

A lot of the examples were taken from the Raku examples.

The REXX language has the "normal"   (as say, compared with PL/I)   overloading of:

  •   the prefix operators   (+ and -)   which are shared with the addition and subtraction operators,
  •   the multiplication operator   (*)   is "shared" with the exponentiation operator   (**),
  •   the "or" operator   (|)   is "shared" with the concatenation operator   (||),
  •   the "and" operator   (&)   is "shared" with the "XOR" (eXclusive OR) operator   (&&),   and
  •   the "negation" operator   (\)   is "shared" with the "not" logical comparison operator,   as in:       if  a\=b  then  ...


Note that some REXXes may also have other characters (glyphs) for the negation operator   (not)   such as:     ^   and/or   ¬   glyphs. <lang rexx>/*REXX pgm shows overloading of some operators: prefix/addition/subtraction/concatenate.*/ say '──positive prefix──' say +5 /* positive prefix integer */ say + 5 /* positive prefix integer */ say ++6 /* positive prefix integer */ say ++ 6 /* positive prefix integer */ say +++7 /* positive prefix integer */ say +++ 7 /* positive prefix integer */ say + + + + 8 /* positive prefix integer */ say + (9) /* positive prefix integer */

say '──negative prefix──' say -1 /* negative prefix integer */ say - 1 /* negative prefix integer */ say --2 /* negative prefix integer */ say -- 2 /* negative prefix integer */ say ---3 /* negative prefix integer */ say --- 3 /* negative prefix integer */ say - - - - 4 /* negative prefix integer */ say - (9) /* negative prefix integer */

say '───addition───' say 3 + 5 /* integer plus integer */ say 3 + (5) /* integer plus integer */ say 3.0 + 0.5e1 /* rational plus number */ say '3' + 5 /* string plus integer */ say 3 + ' 5 ' /* integer plus string */ say 3 + '5' /* integer plus string */ say '3' + '5' /* string plus string */ say '3' + "5" /* string plus string */ say '3.0' + '0.5e1' /* string plus string */

say '──subtraction──' say 3 - 5 /* integer minus integer */ say 3 - (5) /* integer minus integer */ say 3.0 - 0.5e1 /* rational minus number */ say '3' - 5 /* string minus integer */ say 3 - '5' /* integer minus string */ say 3 - ' 5 ' /* integer minus string */ say '3' - '5' /* string minus string */ say '3' - "5" /* string minus string */ say '3.0' - '0.5e1' /* string minus string */

say '──concatenation──' say 3 || 5 /* integer concatenated integer */ say 3 || (5) /* integer concatenated integer */ say 3.0 || 0.5e1 /* rational concatenated number */ say '3' || 5 /* string concatenated integer */ say 3 || '5' /* integer concatenated string */ say '3' || '5' /* string concatenated string */ say "3" || "5" /* string concatenated string */ say '3.0' | | '0.5e1' /* string concatenated string */ say 3 || ' 5 '. /* integer concatenated strings */

say '────abutment────' say 3 5 /* integer abutted integer */ say 3 (5) /* integer abutted integer */ say 3.0 0.5e1 /* rational abutted number */ say '3' 5 /* string abutted integer */ say 3 '5' /* integer abutted string */ say '3' '5' /* string abutted string */ say "3" "5" /* string abutted string */ say 3 ' 5 '. /* integer abutted strings */

say '──multiplication──' say 3 * 5 /* integer multiplied integer */ say 3 * (5) /* integer multiplied integer */ say 3.0 * 0.5e1 /* rational multiplied number */ say '3' * 5 /* string multiplied integer */ say 3 * '5' /* integer multiplied string */ say '3' * '5' /* string multiplied string */ say "3" * "5" /* string multiplied string */ say '3.0' * '0.5e1' /* string multiplied string */

say '──exponentation──' say 3 ** 5 /* integer exponetiated integer */ say 3 ** (5) /* integer exponetiated integer */ say 3 * * 5 /* integer exponetiated integer */ say 3.0 ** 0.5e1 /* rational exponetiated number */ say '3' ** 5 /* string exponetiated integer */ say 3 ** '5' /* integer exponetiated string */ say '3' ** '5' /* string exponetiated string */ say "3" ** "5" /* string exponetiated string */ say '3.0' ** '0.5e1' /* string exponetiated string */

say '────division────' say 3 / 5 /* integer divided integer */ say 3 / (5) /* integer divided integer */ say 3.0 / 0.5e1 /* rational divided number */ say '3' / 5 /* string divided integer */ say 3 / '5' /* integer divided string */ say '3' / '5' /* string divided string */ say "3" / "5" /* string divided string */ say '3.0' / '0.5e1' /* string divided string */

say '─────not────' say \0 /* (not) invert binary */ say \1 /* (not) invert binary */ say \ 1 /* (not) invert binary */ say \ (0) /* (not) invert binary */ say \ 1 /* (not) invert binary */ say \ (0) /* (not) invert binary */ say \\ 0 /* (not) (not) invert binary */ say \ \ 1 /* (not) (not) invert binary */

say '─────or─────' say 0 | 0 /* binary OR'ed binary */ say 0 | 1 /* binary OR'ed binary */ say '0' | "1" /* binary OR'ed binary */ say '1' | 0 /* binary OR'ed binary */ say '1' | (0) /* binary OR'ed binary */

say '─────and────' say 0 & 0 /* binary AND'ed binary */ say 0 & 1 /* binary AND'ed binary */ say '0' & "1" /* binary AND'ed binary */ say '1' & 0 /* binary AND'ed binary */ say '1' & (0) /* binary AND'ed binary */

say '─────XOR────' say 0 && 0 /* binary XOR'ed binary */ say 0 && 1 /* binary XOR'ed binary */ say '0' && "1" /* binary XOR'ed binary */ say '1' && 0 /* binary XOR'ed binary */ say '1' && (0) /* binary XOR'ed binary */

exit 0 /*stick a fork in it, we're all done. */</lang>

output   when using the internal default input:
──positive prefix──
5
5
6
6
7
7
8
9
──negative prefix──
-1
-1
2
2
-3
-3
4
-9
───addition───
8
8
8.0
8
8
8
8
8
8.0
──subtraction──
-2
-2
-2.0
-2
-2
-2
-2
-2
-2.0
──concatenation──
35
35
3.00.5E1
35
35
35
35
3.00.5e1
3 5 .
────abutment────
3 5
3 5
3.0 0.5E1
3 5
3 5
3 5
3 5
3  5 .
──multiplication──
15
15
15.0
15
15
15
15
15.0
──exponentation──
243
243
243
243
243
243
243
243
243
────division────
0.6
0.6
0.6
0.6
0.6
0.6
0.6
0.6
─────not────
1
0
0
1
0
1
0
1
─────or─────
0
1
1
1
1
─────and────
0
0
0
0
0
─────XOR────
0
1
1
1
1

Wren

Library: Wren-date


All of Wren's operators can be overloaded except: &&, ||, ?: and =. It is not possible to create new operators from scratch.

When an operator is overloaded it retains the same arity, precedence and associativity as it has when used in its 'natural' sense.

The standard library contains several instances of overloading the + and * operators which are demonstrated below.

Otherwise, operator overloading can be used without restriction in user defined classes.

However, whilst it is very useful for classes representing mathematical objects, it should otherwise be used sparingly as code can become unreadable if it is used inappropriately. <lang ecmascript>import "/date" for Date

var s1 = "Rosetta " var s2 = "code" var s3 = s1 + s2 // + operator used to concatenate two strings System.print("s3 = %(s3)")

var s4 = "a" * 20 // * operator used to provide string repetition System.print("s4 = %(s4)")

var l1 = [1, 2, 3] + [4] // + operator used to concatenate two lists System.print("l1 = %(l1)")

var l2 = ["a"] * 8 // * operator used to create a new list by repeating another System.print("l2 = %(l2)")

// the user defined class Date overloads the - operator to provide the interval between two dates var d1 = Date.new(2021, 9, 11) var d2 = Date.new(2021, 9, 13) var i1 = (d2 - d1).days System.print("i1 = %(i1) days")</lang>

Output:
s3 = Rosetta code
s4 = aaaaaaaaaaaaaaaaaaaa
l1 = [1, 2, 3, 4]
l2 = [a, a, a, a, a, a, a, a]
i1 = 2 days