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

4.6k views Asked by At

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

3

There are 3 answers

0
Tomas Petricek On BEST ANSWER

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.

2
Jon Skeet On

Have you tried using an else instead?

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

I haven't tried it myself, but it's worth a shot :)

(Also consider using pattern matching instead, of course.)

2
Tim Robinson On

F# has no return statement. The only way to do what you want to here, to choose either one block or the other, is with if .. else:

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

Alternatively, throwing an exception exits the function early:

let myf (mypar: int) =
   if mypar = 2 then
      failwith "invalid argument"

   mypar + 1