tags:

views:

679

answers:

7

So I've been reading the Expert F# book by Apress, mostly using it as a reference when building a toy-ish F# library, but there's one thing I've failed to grasp and that's the "Option" type.

How does it work and what is it's real world usage?

+1  A: 

You use it when a value is optional. One use is to have a kind of 'null reference', e.g.

 val x : int option ref = None

Then you can later update x to Some v. You use it with the match statement, e.g.

 match !x with (* dereference x *)
    None -> (* No value, do something *)
 |  Some v -> (* Value v, do something else *)
TrayMan
+8  A: 

The option type is at least similar to Nullable<T> and reference types in C#. A value of type Option<T> is either None which means there's no encapsuated value, or Some with a particular value of T. This is just like the way a Nullable<int> in C# is either the null value, or has an associated int - and the way a String value in C# is either a null reference, or refers to a String object.

When you use an option value, you generally specify two paths - one for the case where there is an associated value, and one where there isn't. In other words, this code:

let stringLength (str:Option<string>) =
  match str with
  | Some(v) -> v.Length
  | None -> -1

is similar to:

int StringLength(string str)
{
    if (str != null)
    {
        return str.Length;
    }
    else
    {
        return -1;
    }
}

I believe the general idea is that forcing you (well, nearly) to handle the "no associated value/object" case makes your code more robust.

Jon Skeet
+2  A: 

It's used when a function or method should "maybe" or "optionally" return a value. In C# you'd probably return null, or return a Null Object or possibly a Nullable for value types.

The downside of returning null (the most common case) is that it's not type safe: null is an instance of all types, so you get into all kinds of hairy null reference situations later on.

The Option type is a so called disciminated union type with two constructors: None and Some a. None explicitly indicates that you don't have a value. Basically it's the Null Object pattern generalized.

Kurt Schelfthout
+4  A: 

One of the best examples of real-world usage is for the TryParse pattern in .Net. See the first half of

http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!181.entry

for a discussion.

Brian
A: 

@Jon Skeet: You could not use 'val' in that form (Some(val)) at least not in CTP version of F# It gives an error: error FS0010: Unexpected keyword 'val' in pattern... Just use another name that's not reserved

EDIT: thanks, I edited Jon's answer. next time, post this as a comment on Jon's answer, since this is not an answer.

Mehdi Asgari
A: 

To add to the other answers, the Option type is nothing special -- it's just another discriminated union. You could define it yourself in one line:

type 'a Option = None | Some of 'a

The utility, as others have pointed out, is that pattern matching will let you safely deconstruct this, instead of checking for null or using some hack-workaround to indicate if a value isn't really a value.

MichaelGG
A: 

A functional pattern using the option type:

When you need to change parts of a recursive data structure, such as a tree or a list, you will want to reuse as much of the existing data structure as possible. The option type can help you with this. These two functions both replaces all occurrences of the number 5 with 7, but the first one copies the entire tree. The second one does not.

type Tree = Leaf of int
      | Node of Tree * Tree

let rec replace_no_sharing tree =
    match tree with
    | Leaf 5      -> Leaf 7
    | Leaf x      -> Leaf x
    | Node (a, b) -> Node (replace_no_sharing a, replace_no_sharing b)

let replace_with_sharing tree =
    let rec replace_option tree =
        match tree with
        | Leaf 5      -> Leaf 7 |> Some
        | Leaf x      -> None
        | Node (a, b) -> match replace_option a, replace_option b with
                         | None, None     -> None
                         | Some a, Some b -> Node (a, b) |> Some
                         | Some a, None   -> Node (a, b) |> Some
                         | None, Some b   -> Node (a, b) |> Some
    match replace_option tree with
    | None      -> tree
    | Some tree -> tree

It might not be necessary in all cases, but it's a nice technique to know.

Jørgen Fogh