5 people like it.

Preempting NullReferenceExceptions

When interfacing .NET libraries that potentially yield null values, a lot of boilerplate code is required to ensure no NullReferenceExceptions are raised. This is an attempt to avoid such repetition with computation expressions.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
/// strips values of reference types that are null
let denull<'T when 'T : not struct> (x : 'T) =
    // some F# core types encode values with null, ignore those
    let usesNullAsTrueValue =
        if typeof<'T> = typeof<unit> then true
        else
            let attrs = typeof<'T>.GetCustomAttributes(typeof<CompilationRepresentationAttribute>, false)
            if attrs.Length = 0 then false
            else
                let flags = (attrs.[0] :?> CompilationRepresentationAttribute).Flags
                flags.HasFlag CompilationRepresentationFlags.UseNullAsTrueValue

    if obj.ReferenceEquals(x, null) && not usesNullAsTrueValue then None
    else Some x

// define the option monad
type OptionBuilder() =
    member __.Return x = Some x
    member __.Bind(x,f) = Option.bind f x
    member __.ReturnFrom (x : 'T option) = x

let option = new OptionBuilder()

// example
open Microsoft.Win32

option {
    let bkey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
    let! k1 = denull <| bkey.OpenSubKey(@"Software\Microsoft\VisualStudio\11.0")
    let! k2 = denull <| k1.OpenSubKey(@"DialogPage\Microsoft.VisualStudio.FSharp.Interactive.FsiPropertyPage")
    let! switch = denull (k2.GetValue "FsiPreferAnyCPUVersion" :?> string)

    if switch = "False" then
        return! denull (k2.GetValue "FsiCommandLineArgs" :?> string)
    else
        return! None
}
val denull : x:'T -> 'T option (requires reference type)

Full name: Script.denull


 strips values of reference types that are null
val not : value:bool -> bool

Full name: Microsoft.FSharp.Core.Operators.not
val x : 'T (requires reference type)
val usesNullAsTrueValue : bool
val typeof<'T> : System.Type

Full name: Microsoft.FSharp.Core.Operators.typeof
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val attrs : obj []
Multiple items
type CompilationRepresentationAttribute =
  inherit Attribute
  new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
  member Flags : CompilationRepresentationFlags

Full name: Microsoft.FSharp.Core.CompilationRepresentationAttribute

--------------------
new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
property System.Array.Length: int
val flags : CompilationRepresentationFlags
System.Enum.HasFlag(flag: System.Enum) : bool
type CompilationRepresentationFlags =
  | None = 0
  | Static = 1
  | Instance = 2
  | ModuleSuffix = 4
  | UseNullAsTrueValue = 8
  | Event = 16

Full name: Microsoft.FSharp.Core.CompilationRepresentationFlags
CompilationRepresentationFlags.UseNullAsTrueValue: CompilationRepresentationFlags = 8
type obj = System.Object

Full name: Microsoft.FSharp.Core.obj
System.Object.ReferenceEquals(objA: obj, objB: obj) : bool
union case Option.None: Option<'T>
union case Option.Some: Value: 'T -> Option<'T>
Multiple items
type OptionBuilder =
  new : unit -> OptionBuilder
  member Bind : x:'a option * f:('a -> 'b option) -> 'b option
  member Return : x:'c -> 'c option
  member ReturnFrom : x:'T option -> 'T option

Full name: Script.OptionBuilder

--------------------
new : unit -> OptionBuilder
member OptionBuilder.Return : x:'c -> 'c option

Full name: Script.OptionBuilder.Return
val x : 'c
val __ : OptionBuilder
member OptionBuilder.Bind : x:'a option * f:('a -> 'b option) -> 'b option

Full name: Script.OptionBuilder.Bind
val x : 'a option
val f : ('a -> 'b option)
module Option

from Microsoft.FSharp.Core
val bind : binder:('T -> 'U option) -> option:'T option -> 'U option

Full name: Microsoft.FSharp.Core.Option.bind
member OptionBuilder.ReturnFrom : x:'T option -> 'T option

Full name: Script.OptionBuilder.ReturnFrom
val x : 'T option
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
Multiple items
val option : OptionBuilder

Full name: Script.option

--------------------
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
namespace Microsoft
namespace Microsoft.Win32
val bkey : RegistryKey
type RegistryKey =
  inherit MarshalByRefObject
  member Close : unit -> unit
  member CreateSubKey : subkey:string -> RegistryKey + 4 overloads
  member DeleteSubKey : subkey:string -> unit + 1 overload
  member DeleteSubKeyTree : subkey:string -> unit + 1 overload
  member DeleteValue : name:string -> unit + 1 overload
  member Dispose : unit -> unit
  member Flush : unit -> unit
  member GetAccessControl : unit -> RegistrySecurity + 1 overload
  member GetSubKeyNames : unit -> string[]
  member GetValue : name:string -> obj + 2 overloads
  ...

Full name: Microsoft.Win32.RegistryKey
RegistryKey.OpenBaseKey(hKey: RegistryHive, view: RegistryView) : RegistryKey
type RegistryHive =
  | ClassesRoot = -2147483648
  | CurrentUser = -2147483647
  | LocalMachine = -2147483646
  | Users = -2147483645
  | PerformanceData = -2147483644
  | CurrentConfig = -2147483643
  | DynData = -2147483642

Full name: Microsoft.Win32.RegistryHive
field RegistryHive.CurrentUser = -2147483647
type RegistryView =
  | Default = 0
  | Registry64 = 256
  | Registry32 = 512

Full name: Microsoft.Win32.RegistryView
field RegistryView.Default = 0
val k1 : RegistryKey
RegistryKey.OpenSubKey(name: string) : RegistryKey
RegistryKey.OpenSubKey(name: string, permissionCheck: RegistryKeyPermissionCheck) : RegistryKey
RegistryKey.OpenSubKey(name: string, writable: bool) : RegistryKey
RegistryKey.OpenSubKey(name: string, permissionCheck: RegistryKeyPermissionCheck, rights: System.Security.AccessControl.RegistryRights) : RegistryKey
val k2 : RegistryKey
val switch : string
RegistryKey.GetValue(name: string) : obj
RegistryKey.GetValue(name: string, defaultValue: obj) : obj
RegistryKey.GetValue(name: string, defaultValue: obj, options: RegistryValueOptions) : obj
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string

More information

Link:http://fssnip.net/fG
Posted:8 years ago
Author:Eirik Tsarpalis
Tags: nullable types , computation expressions