F# Projects with Visual Studio 2008

I started a few days ago to study F# and I was struggling with some things that were not good documented. I want to share what I’ve learn from that and hopefully that will help others.

After you download and install F# (available at http://research.microsoft.com/fsharp/release.aspx) a new project template will be made available in VS 2008 under Other Porject Types > F# Projects. This is called simply F# Project. When you use that to create a project, it generates a project file with the extension fsharpp. Suppose the project is named demo, then the project file is demo.fsharpp. Initially, it looks like this:

"General" 
{ 
"ProjectIdGuid" = "{4ABF8CC3-3B48-4D3F-8D73-A8BAB9748402}" 
} 
"Configurations" 
{ 
   "Debug|Win32" 
   { 
   "ProjectType" = "EXE" 
   "OptLevel" = "Off" 
   "StrongNamingLevel" = "None" 
   "OutputPath" = "" 
   "SearchPath" = "" 
   "StrongNameFile" = "" 
   "OutputBase" = "" 
   "CustomCommandLine" = "" 
   "CustomCompiler" = "" 
   "DebugCheck" = "TRUE" 
   "CustomCompilationCheck" = "FALSE" 
   "DebugStartMode" = "0" 
   "StartApp" = "" 
   "StartAppPath" = "" 
   "CustomStartupArguments" = "" 
   } 
   "Release|Win32" 
   { 
   "ProjectType" = "EXE" 
   "OptLevel" = "3" 
   "StrongNamingLevel" = "None" 
   "OutputPath" = "" 
   "SearchPath" = "" 
   "StrongNameFile" = "" 
   "OutputBase" = "" 
   "CustomCommandLine" = "" 
   "CustomCompiler" = "" 
   "DebugCheck" = "FALSE" 
   "CustomCompilationCheck" = "FALSE" 
   "DebugStartMode" = "0" 
   "StartApp" = "" 
   "StartAppPath" = "" 
   "CustomStartupArguments" = "" 
   } 
} 
"Files" 
{ 
} 
"ProjStartupServices" 
{ 
} 
"Globals" 
{ 
}

Now, you can add files to the project. The following image shows the available project items:

Available F# project items

You can see among the items the following:

  • F# Source File – a file with extension .fs containing implementation of types and functions
  • F# Interface File – a file with extension .fsi containing declarations/signatures for the implementation in a .fs file
  • ML/F# Source File – a file with extension .ml containing the implmenetation of a module
  • ML/F# Interface File – a file with extension .mli containing the declaration/signature of one or more F# modules

An .ml file is just an .fs file, except that is should contain the implementation of a module. A module is a named collection of definitions. On the other hand, the difference between .mli and .fsi is that the first should be used for declaring modules.

So, let’s add a F# source file to our empty project, and let’s call it main.fs. Visual Studio will populate it with sample code, but ignore that and replace it with:

#light   

open System   

let main() = 
   Console.WriteLine("hello world") 
   Console.ReadKey() 

main()

Well, I’m not going into the details of the syntax here; it’s enough to say that this small program prints “hello world” in the console and waits for the user to press a key. (Build it and run it).But let’s say we want to put the code that prints the message in the console in a separate function:

#light 

open System 

let welcome() = 
   Console.WriteLine("hello world") 

let main() = 
   welcome() 
   Console.ReadKey() 

main()

This has the same result as the previous program. But what if I want that in a separate file? Well, let’s create a new F# source file and call it hellos.fs, and put the code in it:

#light   

open System   

let welcome() = 
   Console.WriteLine("hello world")

with the main.fs file looking like this:

#light 

open System 

let main() = 
   welcome() 
   Console.ReadKey() 

main()

If you build this project, you’ll get the following error:

main.fs(6,1): error: FS0039: The value or constructor ‘welcome’ is not defined.

Looks like the welcome function is not defined. Why? Because we should also have signature file (.fsi) that should declare what hellos.hs is defining. It’s just like a header file in C or C++, containing the declaration of functions or classes.Let’s add a file called hellos.fsi with the following content:

#light   

val welcome : unit -> unit

Building this yields the following error:

hellos.fsi(1,0): error: An implementation of file or module Hellos has already been given. Compilation order is significant in F# because of type inference. You may need to adjust the order of your files to place the signature file before the implementation. In Visual Studio files are type-checked in the order they appear in the project file, and to edit a project file right-click on the project node, unload the project, right-click again, edit the project file manually then the reload the project.

The error message is very explanatory: order of compilation is not ok:

C:\Program Files\FSharp-1.9.3.14\bin\fsc.exe –fullpaths –progress -Ooff -g -o “f:\marius\f#\demo\demo.exe” “main.fs” “hellos.fs” “hellos.fsi”

main.fs is compiled before hellos.fs, and this one before hellos.fsi. In fact, they should be completly reserved. The only way to do that is manually, so you have to unload the project, edit the project file and load it again.If you look at the project file, it contains this:

"Files" 
{ 
   "main.fs" 
   { 
   "ProjRelPath" = "T" 
   } 
   "hellos.fs" 
   { 
   "ProjRelPath" = "T" 
   } 
   "hellos.fsi" 
   { 
   "ProjRelPath" = "T" 
   } 
}

The order of files must be changed to:

"Files" 
{ 
    "hellos.fsi" 
    { 
    "ProjRelPath" = "T" 
    } 
    "hellos.fs" 
    { 
    "ProjRelPath" = "T" 
    } 
    "main.fs" 
    { 
    "ProjRelPath" = "T" 
    } 
}

But if you build it now, you again get an error:

main.fs(6,3): error: FS0039: The value or constructor ‘welcome’ is not defined.

The reason is that a module name is inferred from the name of the hellows.fsi file (by capitalizing it). Thus, we either use the fully qualified name Hellows.welcome(), or apen the Hellos module in file main.fs.

#light 

open System 
open Hellos 

let main() = 
    welcome() 
    Console.ReadKey() 

main()

And now everything works again! You can change the extensions from .fsi to .mli and from .fs to .ml and have the same result.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.