Introducing "use" bindings in F# 1.9.2

F# 1.9 is seeing the rollout of a number of additions to the F# language, and this is the first of a series of blog entries on these.

 For starters, the 1.9.2 release has seen the addition of "use" bindings. Here is a simple example:

    let writePlayList() =

        use outp = File.CreateText(@"playlist.txt")

        outp.WriteLine("Song 1")

        outp.WriteLine("Song 2")

This is entirely equivalent to:

    let writePlayList() =

        using (File.CreateText(@"playlist.txt")) (fun outp ->

            outp.WriteLine("Song 1")

            outp.WriteLine("Song 2")

where the function using has the usual definition in the F# library:

let using (ie : #IDisposable) f =

try f(ie)

finally

match box(ie) with

| null -> ()

| _ -> ie.Dispose()

The general expression form is use id = expr in expr. The "in" is optional according to the rules of the #light syntax, as usual. In other words, "use" thus plays the same role as the C# "using" construct, i.e. gives "deterministic resource reclamation" on both success and error paths of a computation. The disposal logic is inserted at the end of the scope of the bound identifier. In the examples above this is the end of the expression that implements the body of the function writePlaylist. This becomes a little clearer if we write out the implementation of writePlayList in the fully qualified syntax and extra added indentation:

    let writePlayList() =

        use outp = File.CreateText(@"playlist.txt") in

           (outp.WriteLine("Song 1");

            outp.WriteLine("Song 2"))

The main advantage of "use" bindings over calling the "using" function is that binding multiple resources gives the expected behaviour without much effort, e.g.

        use outp1 = File.CreateText(@"playlist1.txt")

        use outp2 = File.CreateText(@"playlist2.txt")

        ...

will work as expected.  "use" bindings can also be used inside sequence expressions and computation expressions. I'll cover these topics in future blog entries.

One possible interesting future extension is to allow the use of "use" bindings inside constructed class definitions. Currently the following is rejected:

    type MyWriter() =

// Note: not allowed

use outp1 = File.CreateText(@"playlist1.txt")

use outp2 = File.CreateText(@"playlist2.txt")

        member x.WriteToBoth(s:string) =

            outp1.Write(s)

            outp2.Write(s)

If this were permitted then it would imply that we automatically implement the IDisposable interface on any type containing "use" bindings, which seems like a reasonable and useful extension to the language.