F# Beginner Function Declaration Gotcha
In this example F# I’m attempting to declare a simple function that just prints “hello”, and then call it three times. What do you think the following code will print?
let printHello = printfn "Hello" printHello printHello printHello
Well the code compiles, but when it runs it just prints “Hello” once. And that’s because
printHello is not actually a function, it’s a value of type “
unit”. We can tell this by hovering over
printHello and seeing the intellisense saying “
val printHello : unit”
So in the
let statement, we call
printfn there and then, and assign it’s return value (
printHello. And the three “calls” to
printHello are not function calls at all. The F# compiler has no problem with these three statements which are effectively no-ops.
So how should we write this function? Well we need to use parentheses to indicate that
printHello is actually a function with no parameters, which is the same as saying it’s a function that takes “
unit”. That looks like this:
let printHello() = printfn "Hello"
Now if we hover over
printHello we see that it is a function that takes
unit and returns
val printHello : unit -> unit”
With this correct definition, we can now call our function three times and get the expected text printed three times. We again need to use parentheses to indicate we are passing
unit into this function:
printHello () printHello () printHello ()
If we forget the parentheses, and just say
printHello on its own, this time we’ll get a compiler warning, telling us “This expression is a function value, i.e. is missing arguments”.
Anyway, hope this helps someone. I managed to make this mistake a few times recently and it took me a long time to spot what I’d done wrong. The moral of the story is to pay attention to the intellisense hints the compiler is giving us.
By the way, Visual Studio Code with the Ionide plugin has a really nice way of visualising the type of each
let statement, which makes it even easier to spot this mistake:
Yup, I fell for something similar very recently. I was writing a non-total function and passing in aYawar Amin
failwith "Error"argument to throw an exception in the error case, except the exception was always getting thrown. I finally realised it was being eagerly evaluated before my function was ever called. D'oh moment.