my tech blog

To iterate is human, to recurse divine

F# Function Overloading

F# is a strongly typed, multi-paradigm (functional and object-oriented) programming language. This post will focus on the functional part of the language and how to handle function overloading or rather the lack of it. Function overloading can be based on either the type argument or the arity number of the function, and I will touch both variants.

Below is a first attempt on function overloading with different arity.

Trying to overload a function in F#
1
2
3
4
5
6
7
module Example1

    let doSomething x =
        x + x

    let doSomething x y =
        x + y

The code above will not compile and the error description is, Duplicate definition of value ‘doSomething’. This would also happen if the arity were the same for both functions.

Trying to function overloading on the argument type
1
2
3
4
5
6
7
module Example2

    let doSomething (x: int) =
        x + x

    let doSomething (y: float) =
        y + y

If you are used to a language like C# this might come as a surprise, since in an object-oriented language like C# we will use a lot of libraries/frameworks that make use of overloading all the time. We could of course use the object-oriented features of F# to create a solution with classes and overload methods for that type just like C#, but now we want to try to be functional purists in F# :-)

First though, why is the code examples above not possible? I’m not able to give a complete and detailed explanation on why this is a problem in F#, but you can read more about it in this thread at Lambda the Ultimate. To summarize, it has to do with the type system and F# isn’t alone with these kind of problems. If the type system would support function overloading the language would probably not be so practical to use anymore.

So how can we handle the function overloading problem?

  • Generics when overloading a function by its type argument
  • Do not overload, name the functions with different names
  • Use the object-oriented features of F#
  • Discriminated unions

Lets examine the fourth option by rewriting the first example.

Example1 rewritten with discriminated union
1
2
3
4
5
6
7
8
9
10
11
module Example1

    type MyDiscriminatedUnion =
        | Value of int
        | Point of int * int

    let doSomething x =
        match x with
        | Value n -> n + n
        | Point (n, m) -> n + m

By introducing the discriminated union, MyDiscriminatedUnion, and passing it as an argument to the function we are possible to have one function that can receive different types and arguments. It would also be possible to use a discriminated union as a return value, if we want to return different types from the function.

One more thing worth mentioning is when you try these examples in the F# repl (or in a F#-script) there are no error message. Instead the environment will shadowing the earlier bindings of the function, so the error message about duplicate definition… will not be shown there.

This post have made me wonder if it’s really a good idea to do function overloading? Perhaps the intention of the code we are writing gets less clear when we are using overloading? So maybe it’s a good thing that overloading isn’t to easy to use, just like mutable variables :-)

It’s worth thinking about.

Comments