Click here to Skip to main content
15,881,803 members
Articles / Programming Languages / F#
Article

Getting Started in F# - A Windows Forms Application

Rate me:
Please Sign up or sign in to vote.
4.78/5 (25 votes)
24 Oct 2008CPOL10 min read 100.7K   826   60   18
Making a Windows Forms project in F#.

Introduction

After procrastinating, I finally decided to sit down and learn to program in F#, since functional programming has always intrigued me. Although I had some experience with Prolog many moons ago, I have done most of my programming using imperative rather than functional or declarative languages, with recent work mostly in C#. Whenever I've learned new languages, there is always an effort required to "get up to speed", but with F#, I found the initial steps were more difficult than I anticipated, due to the lack of a mature IDE, on top of learning a new language and adjusting to a different programming paradigm. I'm still no expert in F#, but perhaps, some of my early stumbling might be of use to others, so they don't have to go through the same pain that I did.

This article describes my steps in getting F# to work for desktop applications. First, I'll describe how I got F# installed with VS 2008, and note some general program and architecture related issues that I ran across. Next, I'll present a simple Windows Forms application that can be used as a template for desktop applications written in F#. Finally, I'll summarize my perceptions of F# as a language, mention the strengths and weaknesses of the current implementation, and explain where I think F# fits in my personal .NET programming toolbox.

As an example application, I put together a simple parser test to be used in an expert system based on fuzzy logic. The parser simply accepts text input, and defines variables and fuzzy sets. The application allows text to be saved into and read from a text file, and is used only in my testing, so it isn't much useful in terms of fuzzy logic. For anyone interested, the actual parser and fuzzy set code is contained in the sample project, but won't be discussed here, since it is beyond the scope of this article. Perhaps, in a future article, I'll describe the rest of the system and show a real word application.

By way of background, the parser is meant to parse a fuzzy set and variable definitions. A variable is declared using either of these forms:

Variable Name of Context [=] Value (i.e. Variable Depth of Water = 250)
Variable Context Name [=] Value (i.e. Variable Water Depth 250)

and a fuzzy set is declared by the form:

FuzzySet Name Context [=] Values (i.e. FuzzySet Deep Water (0,0) (900,1)

In both of the definitions, both a Name and a Context are specified so that ideas with similar names can be differentiated, for example, a Hot Day vs. a Hot Volcano. Both can be measured in temperature units, and have the same name, but the ranges and the meaning of the "Hot" concept is very different.

Getting F# Up and Running

To get started with F#, I downloaded the Microsoft F# CTP (Version 1.9.6.2) from the Microsoft F# website[^], saving the .msi file and then running it to install F#. I accepted all the installation defaults, and it installed without any obvious problems.

Once installed, I opened VS 2008 Professional, and tried to activate the F# add-in according to the Microsoft instructions, but the F# add-in was not on the list under Tools | Add-in Manager. After uninstalling and reinstalling with the same results, I finally discovered that by simply typing Alt+Ctrl+F, the F# Interactive window opened, and apparently the installation had worked just fine the first time, an hour or so earlier. The F# add-in still doesn't show up in the Add-in Manager, but it seems to work just fine.

Once installed and working, using the VS editor with the F# Interactive tool was easy. By simply typing code in the editor, highlighting it, and pressing Alt+Enter, the highlighted code is copied into the interactive F# window, compiled, and run. It's beautiful for trying, testing, and interactively debugging code snippets. Also, in the editor, Intellisense seems to work for most, but not all, of the F# code that I needed.

In trying to code anything more than some trivial tests, however, I ran into two problems which I haven't really found mentioned elsewhere online.

First, even though I was able to pop-up a Windows form, I always had the black DOS/Command window show up as well. Since the F# system doesn't allow you to add a Windows Forms project, you have to manually go to the Project | Properties and set the application type to Windows Application. It appears that the default F# project is always a Console Application.

Second, even though I added references to the standard .NET namespaces (open System.Windows.Forms, etc.), the F# compiler always complained about not finding things like System.Windows.Forms, System.Windows.Drawing, etc. Apparently, VS doesn't automatically add the common references, and those had to be added manually under Solution Explorer | References. With those two items out of the way, using F# was fairly easy, with the rest of my headaches being due to learning a new language and programming paradigm.

Once my project got to the point where I needed several source code files to keep things organized, I ran into several other problems. First, there is no obvious entry point, main function, or other obvious starting place in F# programs. So, how does the compiler know where to start?

Apparently, the F# system simply runs all of the executable statements in the last file compiled. This has two immediate implications:

  • The order of the files in the Solution Explorer makes a difference.
  • Make sure the starting point for your program is in the last file in the project list.

To work around these issues, I took the approach of placing each type in a separate file, pretty much like in C#, and creating a simple file that always is last in the file list called project_name.fs, where project_name is replaced by the actual name of my project. This file is very simple, containing only the following lines, where MyNamespace and MyMainForm() are the namespace and main form names used in a particular application. Note that if you need to do additional things before actually displaying the main form, the code for those can also be inserted before the do Application.Run statement, and might require additional open statements to reference any needed namespaces or modules.

F#
#light

open System
open System.Windows.Forms
open MyNamespace

[<STAThread>]
do Application.Run(new MyMainForm())

Note that the [<STAThread>] line is a .NET attribute that defines the application to run in a Single Thread Apartment. This is required when some of the .NET dialogs are used, such as FileOpenDialog and FileSaveDialog, because they apparently use COM Interop behind the scenes. If you don't use any of those, that attribute won't be needed.

Building a Windows Forms Application

In working with F#, I truly began to appreciate the designers that are available for C#, VB, and other languages supported by VS 2008 and other IDEs. Since form designers are not available for F#, I had to code all windows by hand. To keep things simple (for me), I adopted a coding style illustrated below for part of the MainForm that represents the application.

F#
#light

namespace MyNamespace

open System
open System.Windows.Forms
open System.Drawing

type MainForm() as form = 
    inherit Form()
    // Define private variables
    let mutable fuzzySets = []
    let mutable fuzzyRules = []
    let mutable fuzzyVariables = []
    let mutable fileName = ""
    // Define the controls for this form
    let mainMenu = new MainMenu()
    let mnuFile = new MenuItem()
    let mnuFileOpen = new MenuItem()
    let mnuFileSave = new MenuItem()
    let mnuFileSaveAs = new MenuItem()
    let mnuFileExit = new MenuItem()
    let mnuHelp = new MenuItem()
    let mnuHelpAbout = new MenuItem()
    let label1 = new Label()
    let label2 = new Label()
    let label3 = new Label()
    let lstFuzzySets = new ListBox()
    let lstVariables = new ListBox()
    let txtInput = new RichTextBox()
    let btnCalculate = new Button()
    let dlgFileOpen = new OpenFileDialog()
    let dlgFileSave = new SaveFileDialog()
    let HomeDir = Application.ExecutablePath
    // Private functions
    let rec getVariable ((lst:(Variable list)), vName:string, vContext:string) =
        match lst with
        | [] -> failwith (sprintf "Variable %s.%s not found" vName vContext)
        | x::_ when (x.Name = vName) && (x.Context = vContext) -> x
        | _::t -> getVariable(t, vName, vContext)
    let rec getFuzzySet ((lst:(FuzzySet list)), vName:string, vContext:string) =
        match lst with
        | [] -> failwith (sprintf "FuzzySet %s.%s not found" vName vContext)
        | x::_ when (x.Name = vName) && (x.Context = vContext) -> x
        | _::t -> getFuzzySet(t, vName, vContext)
    let rec prtVars (s:(Variable list)) =
        match s with
        | [] -> ""
        | x::y -> (sprintf "[%s %s = %f]" x.Name x.Context x.Value)^(prtVars y)
    let rec prtFSets (s:(FuzzySet list)) =
        match s with
        | [] -> ""
        | x::y -> (sprintf "[%s %s = %A]" x.Name x.Context x.Def)^(prtFSets y)
    // The constructor simply initializes the form
    do form.InitializeForm

    // member definitions
    member this.InitializeForm =
        // Set Form attributes
        this.FormBorderStyle <- FormBorderStyle.Sizable
        this.Text <- "Fuzzy Logic Parser F# Test"
        this.Width <- 300
        this.Height <- 300
        // Declare Form events
        this.Load.AddHandler(new System.EventHandler 
            (fun s e -> this.Form_Loading(s, e)))
        this.Closed.AddHandler(new System.EventHandler 
            (fun s e -> this.Form_Closing(s, e)))
        // MainMenu
        mnuFile.Text <- "&File"
        mnuFileOpen.Text <- "&Open"
        mnuFileOpen.Click.AddHandler(new System.EventHandler 
            (fun s e -> this.mnuFileOpen_Click(s, e)))
        mnuFileSave.Text <- "&Save"
        mnuFileSave.Click.AddHandler(new System.EventHandler 
            (fun s e -> this.mnuFileSave_Click(s, e)))
        mnuFileSaveAs.Text <- "Save &As"
        mnuFileSaveAs.Click.AddHandler(new System.EventHandler 
            (fun s e -> this.mnuFileSaveAs_Click(s, e)))
        mnuFileExit.Text <- "E&xit"
        mnuFileExit.Click.AddHandler(new System.EventHandler 
            (fun s e -> this.mnuFileExit_Click(s, e)))
        mnuFile.MenuItems.AddRange([| mnuFileOpen; mnuFileSave; 
                mnuFileSaveAs; mnuFileExit |])
        mnuHelp.Text <- "&Help"
        mnuHelpAbout.Text <- "&About"
        mnuHelpAbout.Click.AddHandler(new System.EventHandler 
            (fun s e -> this.mnuHelpAbout_Click(s, e)))
        mnuHelp.MenuItems.AddRange([| mnuHelpAbout |])
        mainMenu.MenuItems.AddRange([| mnuFile; mnuHelp |])
        this.Menu <- mainMenu
        // label1
        label1.Text <- "Fuzzy Sets"
        label1.Location <- new Point(5,2)
        label1.Dock <- DockStyle.None
        label1.AutoSize <- true
        // lstFuzzySets
        lstFuzzySets.Location <- new Point(5,19) 
        lstFuzzySets.Width <- 137
        lstFuzzySets.Height <- 120
        lstFuzzySets.MouseDoubleClick.AddHandler(new MouseEventHandler 
            (fun s e -> this.lstFuzzySets_Click(s, e)))

        ...

        // Add controls to form
        this.Controls.AddRange([| 
                                (label1:> Control);
                                (lstFuzzySets:> Control);
                                (label2:> Control);
                                (lstVariables:> Control);
                                (label3:> Control);
                                (txtInput:> Control);
                                (btnCalculate:> Control)
                               |])
    
    member this.Form_Loading(sender : System.Object, e : EventArgs) =
        lstFuzzySets.Items.Clear()
        lstVariables.Items.Clear()

    member this.Form_Closing(sender : System.Object, e : EventArgs) =
         null

    member this.mnuFileOpen_Click(sender : System.Object, e : EventArgs) = 
        dlgFileOpen.DefaultExt <- "txt"
        if dlgFileOpen.ShowDialog() = DialogResult.OK then
            fileName <- dlgFileOpen.FileName
            txtInput.Clear()
            txtInput.LoadFile(fileName)
        else null

    ...
        
    member this.mnuHelpAbout_Click(sender : System.Object, e : EventArgs) = 
        (new AboutForm()).ShowDialog() |> ignore
        
    member this.lstFuzzySets_Click(sender : System.Object, e : MouseEventArgs) =
        let s = String.split ['.'] (lstFuzzySets.SelectedItem.ToString())
        let text (x:FuzzySet) = sprintf "%s = %A" (x.Name^"."^x.Context) (x.Def)
        MessageBox.Show(text (getFuzzySet(fuzzySets, s.Head, s.Tail.Head)))  |> ignore
        
    ...

Note that the form is defined by inheriting a standard .NET Form. I then defined any private "variables" that will be needed, then all of the controls that are placed on the form. After the controls, I placed the constructor code that is to be executed; in this case, it simply calls the form.InitializeForm function to initialize the form. Finally come all of the member functions that are needed, including all of the event handlers.

Inside the InitializeForm member, each control is set up as needed, and finally all of the controls are added to the form. In addition, all events are defined as members so that they may be organized, coded separately, and called from outside the form. This is similar to how things are automatically organized by the C# and VB designers. Presumably, when designers for F# become available, they would take care of organizing things in a similar way. I have to admit that after doing the controls layout manually, I certainly appreciate the ability to use the designers available for other languages!

There are a couple of things to note in the above code.

First, the event handlers are added to the form and each control using the standard .NET AddHandler, and are defined using anonymous functions in F#. Most of the event handlers take two parameters corresponding to the sender and the EventArgs, denoted by s and e in the anonymous functions.

Second, all of the event handlers expect a unit (void in C#) to be returned. In cases where the result of an F# function is a value other than unit, it's necessary to either explicitly return a unit value using the null keyword or throw away a value using |> ignore.

As you can see, there is really no true functional programming contained in the above code; it's all pretty much straight imperative code, similar to what would be used in C#, with a different syntax. That is mainly because of the dependence on the .NET framework that is inherently imperative. Of course, the ability of F# to handle both imperative and declarative code is one of its strengths, and the ability to program Windows forms shows the imperative side of F#.

For anyone interested, the actual parser and fuzzy set code is contained in the sample project, but won't be discussed here, since it is beyond the scope of this article. Perhaps, in a future article, I'll describe those parts of the system.

Conclusions

After experimenting with F# for awhile and getting somewhat up-to-speed, I have definitely formed some opinions. First, I have to say that F# appears to be a very nice, declarative, functional language, and should find wide application in many areas of scientific and mathematical computing. Since it also allows imperative programming, it is fairly easy to access the objects and features in the underlying .NET Framework, which is a big plus.

Unfortunately, F# in its current implementation is substantially lacking in what I would call "developer tools". Specifically, the lack of form designers and complete Intellisense support in Visual Studio makes it somewhat tedious to use. In addition, some of the default project settings are not very intuitive, and not extremely well documented. Hopefully, both of those problems will be addressed in the near future and F# will be able to take its place along with the other .NET languages as a serious development language.

Aside from the lack of developer tools, programming in F# takes some getting used to, especially if you are used to using imperative languages, as I am. The shift in paradigm from imperative to declarative can be difficult, and F# seems to have so many detailed oddities, that it indeed takes some concerted effort to master. I certainly have not yet mastered it, but I hope to get a book or two on the language on my next trip home and pursue F# much more in the future, because I do definitely see where it will be a handy tool to have in my programming toolbox.

I would recommend that anyone with even the slightest interest learn about F#. It never hurts to expand one's resume, or to stretch the brain into thinking about things from a different perspective.

In addition, I sincerely hope that Microsoft will continue to improve F#, and that additional project templates, form designers, and complete Intellisense support will be provided in the near future. And for my wish list, I'd have to add that being able to design and implement forms in C# (or even VB?), yet call functions written in F# would be wonderful. So far, the only way I've been able to do that has been to compile F# code to a library (*.dll) and then call it from C#. Being able to easily include both C# and F# files in a project, each compiled with the respective compiler and then linked into one application, would allow one to enjoy the best of both worlds.

History

  • 24th October, 2008 - Article submitted; sure enough, right after posting, I had to correct a typo.

License

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


Written By
Engineer Comport Computing
United States United States
I am a software developer specializing in technical and numerical software systems and also a PhD Petroleum Engineer.
I started programming in IBM 1620 machine code in 1967, then FORTAN on CDC mainframes mainframes in 1970. I later used ALGOL, BASIC, FORTH, Pascal,Prolog, C, F#, C#, etc.
I generally use whatever language available thatallows me to accomplish what is neccesary.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Farhad Reza2-Oct-15 6:01
Farhad Reza2-Oct-15 6:01 
QuestionThank you.... Pin
Patil Kishor23-Sep-12 13:43
Patil Kishor23-Sep-12 13:43 
GeneralMy vote of 5 Pin
Patrick Harris25-Feb-12 18:50
Patrick Harris25-Feb-12 18:50 
QuestionVote of 5 Pin
Patrick Harris25-Feb-12 18:50
Patrick Harris25-Feb-12 18:50 
GeneralMy vote of 5 Pin
Tobias Wenig29-Jan-12 7:42
Tobias Wenig29-Jan-12 7:42 
QuestionThanks a lot! Pin
Tobias Wenig29-Jan-12 7:41
Tobias Wenig29-Jan-12 7:41 
AnswerRe: Thanks a lot! Pin
Dr.Walt Fair, PE29-Jan-12 13:12
professionalDr.Walt Fair, PE29-Jan-12 13:12 
GeneralMy vote of 5 Pin
guyet0527-Jul-10 3:53
guyet0527-Jul-10 3:53 
Generalthanks!!!! Pin
TAFIN1-Aug-09 18:41
TAFIN1-Aug-09 18:41 
Generalthanks Pin
Donsw30-Jan-09 6:36
Donsw30-Jan-09 6:36 
Generalcode is large Pin
vpolozov10-Dec-08 4:35
vpolozov10-Dec-08 4:35 
GeneralRe: code is large Pin
Dr.Walt Fair, PE11-Dec-08 15:48
professionalDr.Walt Fair, PE11-Dec-08 15:48 
GeneralRe: code is large Pin
vpolozov12-Dec-08 3:47
vpolozov12-Dec-08 3:47 
GeneralForm designer Pin
TobiasP1-Nov-08 23:51
TobiasP1-Nov-08 23:51 
GeneralRe: Form designer Pin
Dr.Walt Fair, PE3-Nov-08 7:13
professionalDr.Walt Fair, PE3-Nov-08 7:13 
GeneralGood Start Pin
sam.hill24-Oct-08 18:19
sam.hill24-Oct-08 18:19 
GeneralRe: Good Start Pin
Dr.Walt Fair, PE25-Oct-08 6:42
professionalDr.Walt Fair, PE25-Oct-08 6:42 
GeneralRe: Good Start Pin
sam.hill25-Oct-08 9:24
sam.hill25-Oct-08 9:24 

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.