Option type

A.k.a Maybe (Haskell), Optional (Java 8)

Models the absence of a value β†’ In the sense of a possibility for a value to be absent β†’ β‰  unit: used in the case where there is never a value

Defined as a union with 2 cases :

type Option<'Value> =
    | None              // Case without data β†’ when value is missing
    | Some of 'Value    // Case with data β†’ when value is present

Common use cases:

  • Modeling an optional field

  • Turning a partial operation into a total operation

Modeling an optional field

type Civility = Mr | Mrs
type User = { Name: string; Civility: Civility option }

let joey  = { Name = "Joey"; Civility = Some Mr }
let guest = { Name = "Guest"; Civility = None }

β†’ Make it explicit that Name is mandatory and Civility optional

☝ Warning: this design does not prevent Name = null here (BCL limit)

Partial to total operation

  • An operation is partial when no output value is possible for certain inputs.

  • The operation can become total by wrapping the result in an option, None being used when the operation gives no output.

Example 1: inverse of a number

Function

Operation

Signature

n = 0.5

n = 0.0

inverse

Partial

float -> float

2.0

infinity ❓

tryInverse

Total

float -> float option

Some 2.0

None πŸ‘Œ

Example 2: find an element in a collection

  • Partial operation: find predicate β†’ πŸ’₯ when item not found

  • Total operation: tryFind predicate β†’ None or Some item

Benefits πŸ‘

  • Explicit, honest regarding partial operation

    • No special value: null, infinity

    • No exception

  • Forces calling code to handle all cases:

    • Some value β†’ output value given

    • None ..... β†’ output value missing

Control flow

How to test for the presence of the value (of type 'T) in the option?

  • ❌ Do not use if option.IsSome then ... option.Value pattern

  • βœ… Do pattern match the option

  • βœ… Do use Option.xxx functions

Control flow with pattern matching

Example:

Control flow with Option.xxx helpers

Mapping of the inner value (of type 'T) if present: β†’ map f option with f total operation 'T -> 'U β†’ bind f option with f partial operation 'T -> 'U option

Keep value if present and if conditions are met: β†’ filter predicate option with predicate: 'T -> bool called only if value present

Exercise

Implement map, bind and filter with pattern matching

Solution
Bonus questions

Example

Advantages

Makes business logic more readable

  • No if hasValue then / else

  • Highlight the happy path

  • Handle corner cases at the end

πŸ’‘ Alternative syntax more light: ad-hoc computation expressions πŸ“

Option: comparison with other types

  1. Option vs List

  2. Option vs Nullable

  3. Option vs null

Option vs List

Conceptually similar β†’ Option ≃ List of 0 or 1 items β†’ See Option.toList function: 't option -> 't list (None -> [], Some x -> [x])

πŸ’‘ Option & List modules: many functions with the same name β†’ contains, count, exist, filter, fold, forall, map

☝ A List can have more than 1 element β†’ Type Option models absence of value better than type List

Option vs Nullable

System.Nullable<'T> ≃ Option<'T> but more limited

  • ❗ Does not work for reference types

  • ❗ Lacks monadic behavior i.e. map and bind functions

  • ❗ Lacks built-in pattern matching Some x | None

  • ❗ In Fβ™―, no magic as in Cβ™― with the null keyword

Example:

πŸ‘‰ Cβ™― uses Nullable whereas Fβ™― uses only Option. However, Nullable can be required with some libraries, for instance to deal with nullable columns in a database.

Option vs null

Due to the interop with the BCL, Fβ™― has to deal with null objects in some cases.

πŸ‘‰ Good practice: isolate these cases and wrap them in an Option type.

Nullable reference types

Fβ™― 9 introduces nullable reference types: a type-safe way to deal with reference types that can have null as a valid value.

This feature must be activated:

  • Adds <Nullable>enable</Nullable> in your .fsproj

  • Passes --checknulls+ options to dotnet fsi - see FSharp.FSIExtraInteractiveParameters settings in vscode

Then, | null needs to be added to the type annotation to indicate that null as a valid value. It's really similar to nullable reference types: Fβ™― string | null is equivalent to Cβ™― string?, with the usual tradeoff for explicitness over terseness/magic.

πŸ”— More details regarding this feature and how Fβ™― handles nullity (e.g. with AllowNullLiteral attribute): Nullable Reference Types in F# 9.

Last updated

Was this helpful?