Walkthrough: Using Visual F# to Create, Debug, and Deploy an Application

This walkthrough introduces you to the experience of using F# in Visual Studio 2010 together with .NET Framework 4.

In this walkthrough, you will learn how to get started with using Visual Studio 2010 to write F# applications through the example of a historical analysis of United States treasury interest-rate data. You will start with some quick analysis of the data by using the F# interactive window, then write and test some code to analyze the data, and then add a C# front end to explore integrating your F# code with other .NET languages.

Prerequisites

You need the following components to complete this walkthrough:

  • Visual Studio 2010

Note

Your computer might show different names or locations for some of the Visual Studio user interface elements in the following instructions. The Visual Studio edition that you have and the settings that you use determine these elements. For more information, see Visual Studio Settings.

To create an F# script

  1. First, create an F# script. On the File menu, point to New, and then click File. In the New File dialog box, select Script in the Installed Templates list and then select F# Script File. Click Open to create the file, and then save the file as RateAnalysis.fsx.

  2. Use .NET and F# APIs to access data from the Internet site of the United States Federal Reserve. Type in the following code.

    open System.Net
    open System.IO
    
    let url = sprintf "http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=bcb44e57fb57efbe90002369321bfb3f&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn"
    let req = WebRequest.Create(url, Timeout = 10000000)
    let resp = req.GetResponse()
    let stream = resp.GetResponseStream()
    let reader = new StreamReader(stream)
    let csv = reader.ReadToEnd()
    

    Notice the following:

    • Strings and keywords are colorized.

    • Completion lists appear after you type every period (.).

    • You can have Visual Studio complete method names and other identifiers by using the keyboard shortcut CTRL+SPACE or CTRL+J in the middle of an identifier. A completion list appears when you use CTRL+J.

    • When you rest the mouse pointer over any identifier in the code, you see a tooltip that contains information about that identifier.

    • If you press F1 when the cursor is in WebRequest, the expected documentation appears.

    • If you press F1 when the cursor is in let, the expected documentation appears.

    • Types and namespaces from mscorlib.dll, System.dll, and System.Windows.Forms.dll are referenced by default.

    • The Timeout value that is being set here is a property, not a constructor argument. F# allows you to set property values in this manner.

    • If you copy the URL in the example into a browser, you get back a list of comma-separated values that contain dates and interest rates, published by the United States Federal Reserve.

  3. You will now execute the code by using F# Interactive. Select all the code (by using a mouse or by pressing CTRL+A) and right-click, and then click Send to Interactive. (Alternatively, press ALT+ENTER.)

    • If it was not visible already, the F# Interactive window appears.

    • Code executes successfully.

    • The following appears in the F# Interactive window.

      val url : string =
        "http://www.federalreserve.gov/releases/h15/data/business_day/"+[18 chars]
      val req : System.Net.WebRequest
      val resp : System.Net.WebResponse
      val stream : System.IO.Stream
      val reader : System.IO.StreamReader
      val csv : string =
        "  ,Instrument,"U.S. government securities/Treasury constant m"+[224452 chars]
      
      >
      
  4. Next, inspect the data by using F# Interactive. At the F# Interactive prompt, type csv;; and then press ENTER. Type csv.Length;; and then press ENTER. Notice the following:

    • The data is current.

    • F# Interactive displays the value of the string csv and its length, as shown here.

      07/10/2009, 3.32
      07/13/2009, 3.38
      07/14/2009, 3.50
      07/15/2009, 3.63
      "
      > csv.Length;;
      val it : int = 224513
      
    • The following illustration shows the F# Interactive window.

      F# Interactive window

      F# Interactive Window

  5. You will now write F# code to parse CSV (Comma-Separated Values) data. A CSV file is so named because it contains values separated by commas. In the Code Editor, add the following code. As you add each line, select the code added in this section up to that line and press ALT+ENTER to see the partial results. After the other open statements, also add open System.Globalization. Notice the following:

    • IntelliSense gives you helpful information after you type a period, even in the middle of complex nested expressions.

    • When code is incomplete (or incorrect), red wavy underlines indicate that syntactic and semantic errors appear in the code.

    • You create pipelines by using the pipe operator (|>). The pipe operator takes the return value from one expression and uses it as the argument for the function on the next line. Pipelines and F# Interactive allow for easy partial execution of data processing code.

    let interest = 
        csv.Split([|'\n'|])
        |> Seq.skip 8
        |> Seq.map (fun line -> line.Trim())
        |> Seq.filter (fun line -> not (line.EndsWith("ND")))
        |> Seq.filter (fun line -> not (line.Length = 0))
        |> Seq.map (fun line -> line.Split([|','|]))
        |> Seq.map ( fun values ->
            System.DateTime.Parse(values.[0], CultureInfo.CreateSpecificCulture("en-US")),
            float values.[1])
    
  6. You will now give this functionality a name. Remove the series ID bcb44e57fb57efbe90002369321bfb3f from the definition of url and replace it with %s to make the string literal a format string. Add seriesID after the format string. Select all the code, and press TAB. Above the indented block of code, add the following lines of code.

    let loadRates maturity =
        // The following tuples associate various maturity durations, in years,
        // with codes defined for treasury bills by the Federal Reserve.
        let maturitiesMap = Map.ofList [ (1, "e30653a4b627e9d1f2490a0277d9f1ac")
                                        (2, "c66ea77a2e8f0919c5133c7633065908")
                                        (5, "fbb02942bfdbff31a479e98bcbe26388")
                                        (10, "bcb44e57fb57efbe90002369321bfb3f")
                                        (20, "a1ebeb6e84ca6389772dd054dc980191")]
        let seriesID = Map.find maturity maturitiesMap
    

    At the end of the indented block, add interest. Notice the following:

    The code now resembles the following.

    open System.Net
    open System.IO
    
    let loadRates maturity =
        // The following tuples associate various maturity durations, in years,
        // with codes defined for treasury bills by the Federal Reserve.
        let maturitiesMap = Map.ofList [ (1, "e30653a4b627e9d1f2490a0277d9f1ac")
                                        (2, "c66ea77a2e8f0919c5133c7633065908")
                                        (5, "fbb02942bfdbff31a479e98bcbe26388")
                                        (10, "bcb44e57fb57efbe90002369321bfb3f")
                                        (20, "a1ebeb6e84ca6389772dd054dc980191")]
        let seriesID = Map.find maturity maturitiesMap
        let url = sprintf "http://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=%s&lastObs=&from=&to=&filetype=csv&label=include&layout=seriescolumn" seriesID
        let req = WebRequest.Create(url, Timeout = 10000000)
        let resp = req.GetResponse()
        let stream = resp.GetResponseStream()
        let reader = new StreamReader(stream)
        let csv = reader.ReadToEnd()
    
    
        let interest = 
            csv.Split([|'\n'|])
            |> Seq.skip 8
            |> Seq.map (fun line -> line.Trim())
            |> Seq.filter (fun line -> not (line.EndsWith("ND")))
            |> Seq.filter (fun line -> not (line.Length = 0))
            |> Seq.map (fun line -> line.Split([|','|]))
            |> Seq.map ( fun values ->
                System.DateTime.Parse(values.[0], CultureInfo.CreateSpecificCulture("en-US")),
                float values.[1])
        interest
    
  7. You will now use this functionality on new inputs. Select all the code and press ALT+ENTER to execute it by using F# Interactive. At the F# Interactive prompt, call the new loadRates function on other maturity rates: 1, 2, and 5, in years. Notice the following:

    • Previous definitions are not lost in F# Interactive, but new definitions are available.

    • Complex structured data is rendered by special printing functionality.

    • It is easy to create a simple lookup table in F# by adding a list of tuples to the function Map.ofList.

To develop a component by using F#

  • Create a library project to expose the functionality that you have created. On the File menu, point to New and then click Project. In the New Project dialog box, select Visual F# in the Installed Templates list and then F# Library to create a new library project. Give the project the name RateAnalysis. Copy the code that you created previously from RateAnalysis.fsx and paste it into Module1.fs. Change the module declaration in Module1.fs from module Module1 to module RateLoader. In Solution Explorer, rename Module1.fs to RateLoader.fs. Notice the following:

    • The default F# Library template provides a code file that has the extension .fs and a script that has the extension .fsx. You can use the script file to interactively test your library code.

The following illustration shows the New Project dialog box with several available options for F#. The F# Library project template is selected.

F# template options

New Project Dialog with F# Library selected

  1. You will now create an F# class that exposes the desired functionality. In Solution Explorer, right-click the project, point to Add, and then click New Item. In the Add New Item dialog box, select F# Source File. Name the file Analyzer.fs. Right-click Script.fsx in Solution Explorer and then click Move Down. (Alternatively, press ALT+DOWN ARROW.) Paste the following code into Analyzer.fs:

    module RateAnalysis.Analyzer
    
    open RateLoader
    
    /// Provides analysis of historical interest rate data.
    type Analyzer(ratesAndDates) = 
        let rates = 
            ratesAndDates
            |> Seq.map snd
    
        /// Construct Analyzer objects for each maturity category.
        static member GetAnalyzers(maturities) = 
            maturities
            |> Seq.map loadRates
            |> Seq.map (fun ratesAndDates -> new Analyzer(ratesAndDates))
    
        member sa.Min =
            let date, minRate = (Seq.minBy (fun (_, rate) -> rate) ratesAndDates)
            (minRate, date.ToString("d"))
    
        member sa.Max = 
            let date, maxRate = (Seq.maxBy (fun (_, rate) -> rate) ratesAndDates)
            (maxRate, date.ToString("d"))
    
        member sa.Current =
            rates |> List.ofSeq |> List.rev |> List.head 
    

    Notice the following:

    • F# supports object-oriented programming concepts. For more information, see Classes (F#), Inheritance (F#), and other relevant topics in the F# Language Reference.
  2. You will now generate XML documentation comments. In Solution Explorer, right-click the project and then click Properties. On the Build tab, select the XML documentation file check box at the bottom of the page. Notice the following:

    • You can generate XML documentation for any F# assembly.

    • By default, XML documentation is generated into the output path.

  3. To build the project, press CTRL+SHIFT+B or F6. Notice the following:

    • The project builds successfully.

    • The Error List window shows no errors.

    • The output directory contains .dll, .pdb, and .xml files.

    • The Output window displays the following:

      ------ Build started: Project: RateAnalysis, Configuration: Debug Any CPU ------
          C:\Program Files (x86)\Microsoft F#\v4.0\fsc.exe -o:obj\Debug\RateAnalysis.exe -g --debug:full --noframework --define:DEBUG --define:TRACE --optimize- --tailcalls- -r:"C:\Program Files (x86)\Microsoft F#\v4.0\FSharp.Core.dll" -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll" -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Core.dll" -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll" --target:exe --warn:3 --warnaserror:76 --vserrors --utf8output --fullpaths --flaterrors Program.fs RateLoader.fs ValueAnalyzer.fs 
          RateAnalysis -> C:\Users\ghogen\Documents\Visual Studio 10\Projects\RateAnalysis\RateAnalysis\bin\Debug\RateAnalysis.exe
      ========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========
      
  4. To add a C# client application, right-click the solution node, point to Add, and then click New Project. In the Add New Project dialog box, select Visual C# in the Installed Templates list, and then select Console Application. You may have to expand the Other Languages node. Name the project CSharpDriver. Right-click this project's References node and then click Add Reference. On the Projects tab of the Add Reference dialog box, select RateAnalysis and then click OK. Right-click the CSharpDriver project node and then click Set as Startup Project. Type the following code in the body of the Main method of the C# application. Notice the following:

    • You can add project-to-project references to and from C# and F#.

    • F# defined namespaces and types can be used from C# like any other type.

    • F# documentation comments are available in C# IntelliSense.

    • C# can access tuple return values from the F# API. The tuples are .NET Framework 4 Tuple values.

    var maturities = new[] { 1, 2, 5, 10 };
    var analyzers = RateAnalysis.Analyzer.Analyzer.GetAnalyzers(maturities);
    
    foreach (var item in analyzers)
    {
        Console.WriteLine("Min = {0}, \t Max = {1}, \t Current = {2}", item.Min, item.Max, item.Current);
    }
    Console.WriteLine("Press Enter to exit.");
    Console.ReadLine();
    
  5. To debug the application, press F11 to build the application, start the application in the debugger, and step into the first line of executed code. Press F11 several more times until you step into F# code in the body of the GetAnalyzers member. Notice the following:

    • You can easily step from C# code into F# code.

    • Each expression in F# is a step in the debugger.

    • The Locals window shows the values of maturities.

    • Continuing to press F11 steps through the evaluation of the rest of the application.

    • Debugger commands like Run to Cursor, Set Next Statement, Insert Breakpoint, Add Watch, and Go to Disassembly all work as expected.

To deploy an F# application:

  1. In this step, you will set the project to target a different version of the .NET Framework. In Solution Explorer, right-click the F# RateAnalysis project and then click Properties. On the Application tab, change the Target Framework setting to .NET Framework 3.5. Notice the following:

    • F# allows you to target different versions of the .NET Framework.

    • Changing the target framework requires reloading the project.

    • After you change the target framework, some assembly references are no longer enabled in the Add Reference dialog box, and they are unavailable.

  2. In the C# project, you must add a reference to the version of the FSharp.Core assembly that targets .NET Framework 2.0, which also should be used when you target versions 3.0 and 3.5 of the .NET Framework. In Solution Explorer, right-click the References node, and then click Add Reference. On the .NET tab, select the FSharp.Core version 2.0.0.0, and then click OK. Rebuild the solution.

  3. To set the prerequisites, double-click the Properties node in CSharpDriver. On the Publish tab, click the Prerequisites button and, in the Prerequisites dialog box, select the check box for Microsoft Visual F# Runtime for .NET 2.0. Notice the following:

    • F# has a runtime package that is separate from the .NET Framework.

    • This runtime package must be explicitly added as a prerequisite when you deploy applications that use F#.

    • There are two runtime packages available: the 2.0 version for .NET Framework versions 2.0, 3.0, and 3.5, and the 4.0 version for .NET Framework 4.

  4. Deploy the C# application by using ClickOnce. Right-click the CSharpDriver project and click Publish. In the Publish Wizard, click Finish. Run the resulting CSharpDriver.application. Notice the following:

    • The Visual F# Runtime Package is included with the application.

    • Running the application installs the F# Runtime Package and executes the application successfully.

Next Steps

Get started writing F# code by reading Walkthrough: Your First F# Program, or learn about functions in F# by reading Functions as First-Class Values (F#). You can explore the F# language by reading the F# Language Reference.

See Also

Other Resources

Visual F# Walkthroughs

Samples and Walkthroughs (F#)

Change History

Date

History

Reason

June 2011

Updated to reflect changes on the Federal Reserve web site.

Content bug fix.