23

I am looking for a way to get the value of an F# option or use a default value if it is None. This seems so common I can't believe something predefined doesn't exist. Here is how I do it right now:

// val getOptionValue : Lazy<'a> -> Option<'a> -> 'a    
let getOptionValue (defaultValue : Lazy<_>) = function Some value -> value | None -> defaultValue.Force ()

I am (sort of) looking for the F# equivalent of the C# ?? operator:

string test = GetString() ?? "This will be used if the result of GetString() is null.";

No function in the Option module does what I think is a pretty basic task. What am I missing?

dbc
  • 104,963
  • 20
  • 228
  • 340
Nikon the Third
  • 2,771
  • 24
  • 35

2 Answers2

37

You're looking for defaultArg [MSDN] ('T option -> 'T -> 'T).

It's often used to provide a default value for optional arguments:

type T(?arg) =
  member val Arg = defaultArg arg 0

let t1 = T(1) 
let t2 = T() //t2.Arg is 0
Daniel
  • 47,404
  • 11
  • 101
  • 179
15

You could easily create your own operator to do the same thing.

let (|?) = defaultArg

Your C# example would then become

let getString() = (None:string option) 
let test = getString() |? "This will be used if the result of getString() is None.";;

val getString : unit -> string option 
val test : string = "This will be used if the result of getString() is None."

Here's a blog post that goes into a little more detail.

Edit: Nikon the Third had a much better implementation for the operator, so I updated it.

Troy Kershaw
  • 590
  • 3
  • 8
  • 3
    Using an operator seems somewhat nicer than `defaultArg possiblyNone "in case of None"` (although I avoid introducing new operators to avoid confusing my colleagues :). But why didn't you define it simply as `let (|?) = defaultArg`? – Nikon the Third Aug 30 '13 at 13:14
  • Hmm, you make a very good point. I updated my answer to your idea. – Troy Kershaw Aug 31 '13 at 02:15
  • If you do use custom operators like this, how do you share them over your codebase (assuming you have functionality in different namespaces, modules, some types etc...)? Obviously if you have to put them in a module and specify the module name as a prefix it detracts from the simplicity. – piers7 Sep 15 '16 at 04:37
  • @piers7 about sharing, you're right, you have to try to make sure everyone can use these universal operators easily. I would put them in a module called e.g. `Project_imports` and mark it as `[]`; see http://stackoverflow.com/questions/12977466/autoopen-attribute-in-f for more. – Yawar Feb 23 '17 at 03:54