Search code examples
c#functionf#returncontains

F# return in functions


I'm new to F# and playing around a bit, I already did work with C#. One thing that recently confused me a bit was returning values in functions. I do unterstand that everything is an expression (as described here) and that the following code won't compile nor execute:

let foo x =
   if x = 0 then x + 2 //The compiler will complain here due to a missing else branch.
   x - 2

Instead, you would have to write that:

let foo x =
   if x = 0 then x + 2
   else x - 2

I do see why that is and a similar problem also occurs when having a loop.

But consider a function which equals a "contains" function of a list in C#:

public static bool Contains(int num, List<int> list) {
   foreach (int i in list) {
      if (i == num) return true;
   }
   return false;
}

If you would translate that one by one, the compiler will tell you there is missing an else branch and I do unterstand why:

let Contains (num : int) list =
   for i in list do
      if i = x then true //Won't compile due to a missing else branch!
   false

Instead, you'd probably write a recursive function like that:

let rec Contains (num : int) list =
   match list with
   |  [] -> false
   |  head::tail -> head = num || (Contains num tail)

And I'm be fine with that, but in some situations, it is hard to do pattern matching. Is there really no way to do the described above? Couldn't one use a keyword like yield (or something similar to "return" in C#)?


Solution

  • An early return in C# is really just a GOTO. And I would consider that bad style, except for the "bouncer pattern" (validation). The argument there usually is to get rid of mental overload as early as possible. Given F#'s expressiveness, most validation can happen through types, so the otherwise ubiquitous if param = null || invalid(param) return style is not (or at least less) needed. Once you embrace the functional style, you'll find that there is less and less need for it. If needed, you can always simulate the goto :-)

    exception Ret of string
    
    let needsEarlyReturn foos =
        try
            for f in foos do
                if f > 42 then Ret "don't do" |> raise
                // more code
                if f > 7 then Ret "that ever" |> raise
            "!"
        with Ret v -> v