Reputation: 1872
Assuming I have the following pseudo-C# code:
TResult MyMethod()
{
var firstTry = SomeExpensiveComputation1();
if (firstTry.IsSuccessful) return firstTry;
var secondTry = SomeExpensiveComputation2();
if (secondTry.IsPartiallySuccessful)
{
var subTry1 = SomeExpensiveComputationOn2_1(secondTry);
if (subTry1.IsSuccessful) return subTry1;
var subTry1 = SomeExpensiveComputationOn2_2(secondTry);
if (subTry1.IsSuccessful) return subTry1;
}
return LastExpensiveComputationThatNeverFails();
}
If I were to do this in F#, it'd look like this:
let MyMethod () =
let firstTry = SomeExpensiveComputation1 ()
if firstTry.IsSuccessful then firstTry else
let secondTry = SomeExpensiveComputation2 ()
if secondTry.IsSuccessful then
let subTry1 = SomeExpensiveComputationOn2_1 ()
if subTry1.IsSuccessful then subTry1 else
let subTry2 = SomeExpensiveComputationOn2_2 ()
if subTry2.IsSuccessful then subTry2 else LastExpensiveComputationThatNeverFails ()
else
LastExpensiveComputationThatNeverFails()
As you can see above, I had to repeat LastExpensiveComputationThatNeverFails
twice. This doesn't have to be a method call, it can be many lines of inline computations (e.g. try to get some value from cache, if it doesn't exist calculate it.) One could refactor the code into another function, but I still don't like how the same code, even if it's just one line, has to be written twice (or more), as it leads to duplication and messy maintenance. What is the correct way to write such code in F#?
Upvotes: 1
Views: 111
Reputation: 11577
I think it's fine to make LastExpensiveComputationThatNeverFails
a local function that is called whenever the result is needed.
However, one could also change the operations to return Option<_>
and use the built-in combinator functions.
let MyMethod () =
SomeExpensiveComputation1 ()
|> Option.orElseWith
( fun () ->
SomeExpensiveComputation2 ()
|> Option.bind (fun _ -> SomeExpensiveComputationOn2_1 () |> Option.orElseWith SomeExpensiveComputationOn2_2)
)
|> Option.orElseWith LastExpensiveComputationThatNeverFails
Option.orElseWith LastExpensiveComputationThatNeverFails
is only executed if the previous result is None
which it will be upon failure.
Upvotes: 3