Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / F#

F#4: Let / Use / Do Bindings

5.00/5 (3 votes)
3 Mar 2014CPOL4 min read 16.3K  
Let / Use / Do Bindings in F#

In this post, we will look at F# bindings, in particular, we will look at Let / Use / Do. Now you may be asking yourself what a binding is, and since we have not really covered it yet, now would be a good time to talk about this.

Quite simply, a binding associates an identifier with a value or function.

Let

You use the let keyword to bind a name to a value or function. There are actually subtly different uses of Let, where one is declared as a top level in a module, and then another is where we define some sort of local context. Here is an example of both of these:

F#
module DemoModule =

    let someFunction =
        let a = 1
        let b = 2
        a * b

We would be able to access the someFunction using a fully qualified name such as DemoModule.someFunction, but the nested Let bindings (a,b) are only accessible to the top level Let binding. You would typically see more cases where we are using the Let binding to declare some inner module values, so let's concentrate our efforts there (though it is still important to know you can use Let at module level).

So let’s have a look at a few more examples:

F#
let aString ="this is a string"
let aInt = 12
let aDecimal = 12.444
let aPiFunction () = Math.PI
let aSquareRootFunction (x) = Math.Sqrt(x)
let aFullyTypedSquareRootFunction (x :float) = Math.Sqrt(x)
let a,b = "a","tuple"

It can be seen that we are able to use the Let binding, to bind to numerous values, that can be of various types such as:

  • Integer
  • Decimal
  • A Function with no input parameters
  • A Function with input parameters (where the F# type inference system will correctly choose the Type)
  • A Function which has fully qualified parameter types
  • A tuple (a tuple of String * String in this case)

Another place you may see a Let binding is in a class, but we will cover this in more detail in a later article in this series.

You can read more about the Let binding using MSDN.

Use

The Use binding is quite similar to the Let binding in that it is binding a value to the result of an expression. The key difference is that the Use binding is designed to work with IDisposable types and will automatically Dispose of the value when it is no longer in scope. This is quite similar to the .NET using keyword, though I do not believe that the F# Use binding will be exactly the same as the .NET using keyword semantics, as the .NET using keyword is really a try / finally with a Dispose() call in the finally.

We have already seen an example of the Use binding in the last post we did on Formatting Text, but just to remind ourselves, let's have a look at that again:

F#
use sw = new StreamWriter(@"c:\temp\fprintfFile.txt")
fprintf sw "This is a string line %s\r\n" "cat"
fprintf sw "This is a int line %i" 10
sw.Close()

In this example, the Use binding ensures that the StreamWriter will have its Dispose() method called after the sw.Close() call seen above.

A Couple of Points

Use Only Works with IDisposables, and you will get a compilation error, should you try and use it with anything else, as shown below:

image

As the Dispose() method is called at the end of a Use binding, care should be taken to not return a value that was bound using a Let.

i.e : Don’t do this:

F#
let Write =
    use sw = new StreamWriter(@"c:\temp\fprintfFile.txt")
    sw

If you absolutely need to pass an IDisposable around that is part of a Use binding, you can use a callback instead. So something like this would work, but I would stop and ask you have you got your design right if you are doing this sort of thing?

F#
let Write callback =
    use sw = new StreamWriter(@"c:\temp\fprintfFile.txt")
    fprintf sw "Write is writing to the StreamWriter"
    callback sw
    sw

let callback sw = fprintf sw "sw is the StreamWriter"
let disp = Write callback

Do

Use a do binding to execute code without defining a function or value. A Do binding MUST always return Unit (no value / void essentially). In a lot of cases, you will be able to omit the Do binding value and things will work just as expected.

Here are some examples of using the Do binding:

F#
do printf "doing the do"
//oh oh not a unit
do printf "print a sum %i" 1 + 1
do 1 + 1

If I instead show you a screen shot of the code above, you will see that the compiler complains if you try and use Do with a Non Unit result.

image

You have 2 choices as the error message states, which is to either:

  1. Use the forward pipe operator to pipe the results to ignore
  2. Create a Let binding

I have shown an example of each of these below:

F#
let x = 1 + 1
do printf "print a sum %i" x
do (1+1 |> ignore)

You can read more about the Let binding using MSDN.

Let! Use! and Do!

Although I don’t want to discuss these just yet, you may on occasion see Let! / Use! / Do!, and when you do, these are part of what is known as a computation expression. The place you are most likely to see these is within F#s Asynchronous Workflows which we will be going through in one of the final articles. If I am man enough, I may even attempt to explain how you can create your own “Computation Expression” though they are quite an abstract concept and are quite an advanced topic, so now is not the time for them.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)