Skip to content

Instantly share code, notes, and snippets.

@mrange
Last active October 3, 2016 09:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrange/242ac7dfc389a9a42a535965f334c308 to your computer and use it in GitHub Desktop.
Save mrange/242ac7dfc389a9a42a535965f334c308 to your computer and use it in GitHub Desktop.
Parameter validation using F# Active Patterns

An interesting but rather unknown usage of Active Patterns in F# is that they can be used to validate and transform function arguments.

Consider the classic way to do argument validation:

    // val f : string option -> string option -> string
    let f v u =
      let v = defaultArg v "Hello"
      let u = defaultArg u "There"
      v + " " + u

    // val g : 'T -> 'T (requires 'T null)
    let g v =
      match v with
      | null  -> raise (System.NullReferenceException ())
      | _     -> v.ToString ()

Typically we add code in the method to verify that arguments are correct. Using Active Patterns in F# we can generalize this and declare the intent in the argument declaration.

The following code is equivalent to the code above:

    let inline (|DefaultArg|) dv ov = defaultArg ov dv

    let inline (|NotNull|) v =
      match v with
      | null  -> raise (System.NullReferenceException ())
      | _     -> v

    // val f : string option -> string option -> string
    let f (DefaultArg "Hello" v) (DefaultArg "There" u) = v + " " + u

    // val g : 'T -> string (requires 'T null)
    let g (NotNull v) = v.ToString ()

For the user of function f and g there's no difference between the two different versions.

    printfn "%A" <| f (Some "Test") None  // Prints "Test There"
    printfn "%A" <| g "Test"              // Prints "Test"
    printfn "%A" <| g null                // Will throw

A concern is if Active Patterns adds performance overhead. Let's use ILSpy to decompile f and g to see if that is the case.

    public static string f(FSharpOption<string> _arg2, FSharpOption<string> _arg1)
    {
      return Operators.DefaultArg<string>(_arg2, "Hello") + " " + Operators.DefaultArg<string>(_arg1, "There");
    }

    public static string g<a>(a _arg1) where a : class
    {
      if (_arg1 != null)
      {
        a a = _arg1;
        return a.ToString();
      }
      throw new NullReferenceException();
    }

Thanks to inline the Active Patterns adds no extra overhead compared to the classic way of the doing argument validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment