Click here to Skip to main content
15,879,490 members
Articles / Programming Languages / F#

F#4: Let / Use / Do Bindings

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
3 Mar 2014CPOL4 min read 15.9K   1   2
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)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
SuggestionLittle more addition here. Pin
VallarasuS3-Mar-14 10:24
VallarasuS3-Mar-14 10:24 
let:

let bindings can be used at any level, and can bind 'almost' everything functions/values/any types. Can bind a different function/values to an identifier in inner block which will out scopes the outer binding in inner block. For CSharpers its 'var' keyword with Func/Func<> and Action/Action<> put together.

let add x y = x + y

If you haven't noticed, it doesn't has any type indications associated with parameters, f# does the magic to infer the type for you. That doesn't stops you from doing it, But if you don't generics come for free, you might have to indicate the type if the type system can't do it.

Here is an awesome stuff called 'currying' for you.

let increment = add 1 // let increment y = add 1 y

do:

One immediate use comes to my mind is do bindings are useful when you need to initialize something, after you construct a type / module.

If you find it interesting fsharp has more to feed your curiosity.
Regards Vallarasu S | BreakingDotNet.blogspot.com

GeneralRe: Little more addition here. Pin
Sacha Barber3-Mar-14 10:32
Sacha Barber3-Mar-14 10:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.