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.
You use the let keyword to bind a name to a value or function. There are actually subtlety 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:
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 lets 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
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:
- 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 : http://msdn.microsoft.com/en-us/library/dd233238.aspx
The Use binding is quite similar to the Let binding in that it 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 lets have a look at that again
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
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:
let Write = use sw = new StreamWriter(@"c:\temp\fprintfFile.txt") sw
If you absolutely need to pass an IDisposable around that are part of a Use binding, you can use a callback instead. So something like this would work, but I would stop and ask yourself have you got your design right if you are doing this sort of thing
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
Use a do binding is used 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
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.
You have 2 choices as he error message states, which is to either
- Use the forward pipe operator to pipe the results to ignore
- Create a Let binding
I have shown an example of each of these below:
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 : http://msdn.microsoft.com/en-us/library/dd393786.aspx
Let! Use! ad 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 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.