.netf#functional-programmingreturnunit-type

F#: More return points in functions, how to handle them?


I have a problem when returning values in complex functions. Examples are always better:

Consider the following function:

let myf (mypar: int) =
   mypar + 1

Well no probel here, this function is compiled correctly and the signature is:

val myf: int -> int

OK, well. Now consider this code:

let myf (mypar: int) =
   if mypar = 2 then
      4 (* ERROR *)
   mypar + 1

This does not work:

This expression was expected to have type unit but here has int

This error is raised everytime I try to return from my function when I am inside a if, a while a for or every other block. I thought that the problem was assuring that all possible return paths return the same type, but here I do not understand what happens.

Please note that if I insert a () unit everything works for example:

let myf (mypar: int) =
   if mypar = 2 then
      () (* No error *)
   mypar + 1

But that unit does not make my function return!!! it continues!!! Furthermore, could you please explain me how F# handles this???

Thankyou


Solution

  • To add some more details, the problem with your approach is that everything in F# is an expression. This makes it a lot easier to reason about your programs (because you don't need to keep track of the currently executing statement), but it means that you always have to write a complete expression.

    If you try to write something like return, it would be as if you wrote the following in C# (This probably explains why F# doesn't allow this kind of things):

    int a = 10 + (3 * (return 10; 2));
    return a;
    

    Why didn't you get error when you wrote if .. then ()? The () expression creates a value of type unit that is special, because it has only one valid value. F# allows you to write if .. then without else when returning unit, because it can figure out that the else branch has to return the only existing unit value, so it sees your code as:

    if something then ()
    else () // implicitly added by the compiler
    

    The only difference is throwing an exception (using raise) which behaves just like in C#. You could break out of a function using exception, but it is much better idea to rewrite the code to have a complete valid expression.