Overloading methods are common practice in many OOP languages, but in functional languages (using a functional style) not only is this not common practice, sometimes it’s not even possible. This short post highlights why and shows the alternatives.
- A simple understanding of an OOP language (C# or Java)
- A simple understanding of a functional programming language. (Code examples are in F#)
As always we need some code to talk about. Here’s some OOP style using F#
1 2 3 4 5 6 7 8 9 10 11 type LibraryClass() = member this.PrintNumber (myNumber: int): unit = printfn "%f" (float myNumber) member this.PrintNumber (myNumber: float): unit = printfn "%f" (float myNumber) // Using class let myLibrary = new LibraryClass() myLibrary.PrintNumber 10 myLibrary.PrintNumber 10.0
No surprises here, everything works as expected. So what happens if we want to write this in more of a functional style. Here’s a first attempt:
1 2 3 4 5 6 module Library = // Does not compile let PrintNumber (myNumber: int) = printfn "%f" (float myNumber) let PrintNumber (myNumber: float) = printfn "%f" (float myNumber)
This does not compile and gives and the error Duplicate definition of value ‘PrintNumber’. Argh, at first glance this would appear like F# is missing some basic machinery.
The reason why this is happening is that the F# compiler cannot determine the difference between the functions. Either the name needs to be changed or the number of parameters. If each function is rather different in nature then changing the name might be a better way to go. However, if your design is pretty good, then chances are these functions should have the same name.
Here is the final effort and fortunately this code compiles:
1 2 3 4 5 6 7 module Library = let inline printNumber myNumber = printfn "%f" (float myNumber) Library.printNumber 10 Library.printNumber 10.0
inline comes to the rescue.
inline can be used to relax the type inference on a function, despite the implementation not doing anything related to this. When the compiler reads
inline, instead of creating a function, it replaces the caller with the function itself, ie it ‘inlines’ the function.
inline can also be used for performance-related tasks as a last effort. For a full read see Inline Functions.
As a result, when a function is inlined, its type inference is the most general it can be. I think a quote is relevant here
With great power comes great responsibility
inline sparingly, only to make the code more readable. As a guideline, the types of the inlined function should still have some restriction, not
obj -> obj. If you have this as a problem, find a way to restrict the types. Perhaps by using a discriminated union (DU) and passing that through, but each case will differ. Now you can use this with iOS and Android, where overriding and overloading typically can’t be avoided. Android and F# are at last friends