The Applied Games Group Blog

New stuff directly from Microsoft Research.

F# Custom Exceptions

F# Custom Exceptions

  • Comments 3
  • Likes

This post tries to explore exception handling in F# with custom exception types.

Some background on .Net exceptions

  • Exceptions happen: "Programs must be able to uniformly handle errors that occur during execution. The common language runtime greatly assists the design of fault-tolerant software by providing a model for notifying programs of errors in a uniform way. All .NET Framework operations indicate failure by throwing exceptions." - .NET Framework Developer's Guide: Handling and Throwing Exceptions.
  • Deal with them: "To build successful and flexible applications that can be maintained and supported easily, you must adopt an appropriate strategy for exception management. You must design your system to ensure that it is capable of the following: 
    • Detecting exceptions.
    • Logging and reporting information.
    • Generating events that can be monitored externally to assist system operation."
    - Microsoft patterns & practices: Exception Management Architecture Guide.
  • But: "Do not rely on exceptions in your code. Exceptions can cause performance to suffer significantly, so you should avoid using them as a way to control normal program flow. If it is possible to detect in code a condition that would cause an exception, do so rather than catching the exception itself and handling the condition."  - Microsoft TechNet: Developing High-Performance ASP.NET Applications.
  • And: "In most cases, use the predefined exceptions types. Define new exception types only for programmatic scenarios. Introduce a new exception class to enable a programmer to take a different action in code based on the exception class." - .Net Framework Developers Guide - Best Practices for Handling Exceptions.

Quick guide to F# exception handling keywords

F# C# equivalent Description
raise exception throw exception; Throws the specified exception.
rethrow () throw; Rethrows the current exception unchanged from a catch block.
try ... with try-catch Filters exceptions.
try ... finally

try-finally

Guarantees execution of specified final block.
failwith string   Throws a Failure exception with the specified string.

F#'s predifined library exceptions

Exception Usage Description
Failure of string failwith "Bang!" General failure.
InvalidArgument of string invalid_arg "arg1" Invalid argument exception.
exn raise (new exn("Bang!")) System.Exception type abbreviation.

Filtering Failures

Exceptions are caught/filtered using pattern matching and a Failure exception can be matched like a discriminated union type:

    try failwith "Too many errors"
    with | Failure s -> printf "%s" s
Outputs: "Too many errors"

Some reusable System.Exception based classes

Frequently it is possible to simply reuse existing exception classes in your code:

Exception class Thrown when...
ApplicationException a non-fatal application error occurs.
ArgumentNullException a null reference is passed to a method that does not accept it.
ArgumentOutOfRangeException the value of an argument is outside the allowable range of values.
FormatException the format of an argument does not meet the parameter specifications.
InvalidOperationException a method call is invalid for the object's current state.
NotImplementedException a requested method or operation is not implemented.

Custom F# exceptions

In some cases we have a pragmatic reason to introduce our own custom exceptions, for example if we define our own data layer we may want to have an exception type for this layer. In the same way the .Net framework defines the SqlException class  for SQL specific exceptions. F# Custom exceptions provide an extremely concise form definition like discriminated unions which makes them simarly easy to pattern match against:

  • Defining a custom F# exception
    exception AppException of string
  • Raising a custom F# exception 

    raise (AppException "Bang!")
  • Catching a custom F# exception

    try raise (AppException "Bang!") with | AppException s -> printf "%s" s

    Outputs: "Bang!" 

  • Reading the Message property value of a custom F# exception

    try raise (AppException "Bang!"with | e -> printf "%s" e.Message

    Outputs: "Exn+AppException[System.String]..."

So this all seems to make F# custom exceptions great for F# projects which are self-contained or which have F# projects as final consumers. However F# custom exceptions current concise construction syntax does not allow us to pass the values for the Message and InnerException properties to the underlying System.Exception. It is possible to override the System.Exception.Message property but then again we lose a lot of our original conciseness. So I think this means then that F# custom exceptions are probably not suitable if you are going to be throwing an exception out of your F# project to say a C# project or to a vanilla exception logging mechanism. 

An Alternative - custom exceptions using plain old inheritance

You will see below custom exceptions using inheritance are also pretty concise for definition and for pattern matching and also allow you to easily initialize the System.Exception's Message and InnerException properties. Here we make good use of F#'s constructed classes, optional parameters and inheritance facilities:

  • Defining an F# custom exception using inheritance
    type VanillaException (message:string, ?innerException:exn) =
        inherit ApplicationException (message, 
            match innerException with | Some(ex) -> ex | _ -> null)            
  • Pattern matching exception types in F# 
    try raise (new VanillaException("Vanilla!")) 
    with 
    | Failure s -> printf "%s" s
    | :? VanillaException as e -> printf "%s" e.Message
    | e -> e.Message
    Outputs: "Vanilla!"

So it appears then that this approach is more suited to F# originated frameworks and libraries that expose exceptions to other languages and/or logging frameworks. Finally I would recommend that in this case that you catch any F# custom exceptions at the application boundary including Failure exceptions and rethrow them as vanilla .Net exceptions.

Phil Trelford

Further reading check out: Expert F# Chapter 4 Introducing Imperative Programming.

Comments
  • Phil Trelford! You worked on Halo!!!!

    This is Chris Graeme's mate, need to get in touch!

  • this may be MONO-specific behavior (i don't have a windows machine on which to test) but it should be noted that tossing custom (or extended) exceptions *out* of F# code causes issues -- for example: in the case of async code blocks you can safely let a .NET/C# exception 'fall through', but an F# exception that falls through will do so with nasty side effects (where "side effect" means SIGSEGV.)

    the last sentence of this post is good advice: "catch any F# custom exceptions at the application boundary including Failure exceptions and rethrow them as vanilla .Net exceptions."

  • Hi

    I'm a student in iran.i need information about F#. can you send for me information about F#(abstract,writeablity,relabelity,Exception Handling,type cheking,...)?

    my mail is eng1computer@yahoo.com

    thanks of you

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment