I would write an orElse
function. Then you could do this:
let orElse f = function
| None -> f()
| Some _ as x -> x
let getIndex connDB strTable =
getIndexByPrimaryKey connDB strTable
|> orElse (fun () -> getIndexByCluster connDB strTable)
|> orElse (fun () -> getIndexByFirstColumns connDB strTable)
|> orElse (fun () -> getIndexByOnlyUnique connDB strTable)
|> orElse (fun () -> getAnyUniqueIndex connDB strTable)
Or, if you prefer "workflow" syntax (and need this often), this:
module OrElse =
let bind f = function
| None -> f()
| Some x -> Some x
let combine m1 m2 =
m1 |> bind (fun () -> m2)
type OrElseBuilder() =
member x.Zero() = None
member x.Return(v) = Some v
member x.Bind(m, f) = bind f m
member x.ReturnFrom(m) = m
member x.Combine(m1, m2) = combine m1 m2
member x.Delay(f) = f()
let orElse = OrElseBuilder()
will let you state it even more consicely:
open OrElse
orElse {
return! getIndexByPrimaryKey connDB strTable
return! getIndexByCluster connDB strTable
return! getIndexByFirstColumns connDB strTable
return! getIndexByOnlyUnique connDB strTable
return! getAnyUniqueIndex connDB strTable
}
Since you're passing the same args to every function, pad's solution is likely as concise as possible. These solutions address the general problem of replacing nested match x with Some _ as v -> v | None -> ...
.
EDIT
Extending Tomas' idea, you could use a general-purpose active pattern for "patternizing" functions:
let (|FN|_|) f x = f x
Then do:
match connDB, strTable with
| FN getIndexByPrimaryKey name -> Some name
| FN getIndexByCluster name -> Some name
| FN getIndexByFirstColumns name -> Some name
| FN getIndexByOnlyUnique name -> Some name
| args -> getAnyUniqueIndex args
This requires one slight change to your functions: the args must be in tupled form.