F # 4: Let / Use / Do

In this post we will look at bindings in F #, in particular we will look at Let / Use / Do. Now you may be asking yourself what bindings are, and since we have not yet examined them, now is the time to talk about it.



Simply put, a binding associates an identifier with a value or function.



Let



You use the let keyword to associate a name with a value or function. Actually there is a subtle different use of Let, where one is declared at the top level in the module and then the other where we define some local context. Here is an example of both of them:



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





We could access someFunction using a fully qualified name, such as DemoModule.someFunction, but nested Let (a, b) bindings are only available for Let's top level. Usually you see more cases when we use the Let binding to declare some values ​​of the internal module, so let's concentrate our efforts there (although it is important to know that you can use Let at the module level).



So let's 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"
      
      





You can see that we can use the Let binding to bind to multiple values, which can be of various types, such as:





Elsewhere, you can see the Let binding in the class, but we will discuss this in more detail in the next article in this series.



You can read more about Let binding on MSDN.



Use



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



We've already seen the Use binding example in the last post on text formatting, but just to remind ourselves, let's take a look at this one more time.



 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 guarantees that the StreamWriter method will call its Dispose () method after calling sw.Close (), shown above.



Use only works with IDisposables, and you will get a compilation error if you try to use it with something else, as shown below:



image



Since the Dispose () method is called at the end of the Use binding, care should be taken not to return the value that was associated with Let.



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





If you absolutely need to pass IDisposable back, which are part of the Use binding, you can use the callback instead. Something like this will work, but I would stop and ask myself if you developed your design correctly if you do such things:



 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



The do binding is used to execute code without defining a function or value. A Binding MUST always return Unit (no value / void). In many cases, you can omit the Do binding and everything will work as expected.



Here are some examples of using Do bindings.



 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 screenshot with the code above, you will see that the compiler will complain if you try to use Do with a non-Unit result.



image



You have two options:





I showed an example of each of them below:



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





Let! Use! Do!



Although I don’t want to discuss them yet, sometimes you may accidentally see Let! Use! Do !, and when you do this, this is part of what is called a computational expression. You will most likely see this in F # asynchronous workflows, which we will cover in one of the final articles. If I understand enough, I can even try to explain how you can create your own “Computational Expression”, although they are a rather abstract concept and a rather complicated topic, so now is not the time for them.



All Articles