StandardML doesn't allow function overloading, and for a good reason (it makes type inference decidable and allows you to never write any type annotations). However, for convenience arithmetic functions are magically overloaded and work with both integers and floating point numbers. Compilers don't expose this functionality to the end user, but deep inside they can do it, and I decided to dig into MLton to find out what it looks like and how to do it myself.
It turned out that they didn't hide it too deep. Reading the standard library source revealed that there is _overload keyword that is not allowed unless you build your program with a special compiler switch.
For demonstration I wrote a program that uses a ^+ operator that works like ^ (concatenation) for string and like + (addition) for integers.
Download the file: mlton-overload.sml.
signature BIZZAREOP_INT = sig val ^+ : int * int -> int end structure BizzareOpInt : BIZZAREOP_INT = struct val ^+ = fn (x, y) => x + y end signature BIZZAREOP_STRING = sig val ^+ : string * string -> string end structure BizzareOpString : BIZZAREOP_STRING = struct val ^+ = fn (x, y) => x ^ y end (* Magic begins here *) _overload ^+ : ('a * 'a -> 'a) as BizzareOpInt.^+ and BizzareOpString.^+ (* Magic ends here *) infix 6 ^+ val () = print ("hello " ^+ "world\n") val () = print ((Int.toString (3 ^+ 4)) ^ "\n")
Compiling it with default options fails with a very clear error message:
$ mlton ./test.sml Error: test.sml 17.1. _overload disallowed.
In build scripts I've found another bit of magic required to get it to work, a special build option.
$ mlton -default-ann 'allowOverload true' ./test.sml $ ./test hello world 7