In some prior blog entries, I highlighted some features of the project system that enable you to use and organize multiple files as well as multiple projects within your solution. In my next blog entries, I want to continue showing you some more features along these lines, but now with more of an eye towards pragmatics, including some advice on how to choose among different ways to organize your code.

Multiple files in F#, and the implicit module

Suppose I have a tiny F# source code file like this:

let AnyAsString x =

sprintf "%A" x

This is a surprisingly handy function (we’ll see why shortly) – it takes any F# value and returns a reasonably-formatted human-readable string version of that value. (You can read more about the various F# printf functions and specifiers here.) Suppose I want to call this function from a different source file further down in my project – how do I do it? Just trying to call it directly doesn’t work; for example if I write

AnyAsString [ 1 ; 2 ; 3 ]

in a different file, I get

The value or constructor ‘AnyAsString’ is not defined .

The reason is that, if you write code at the "top level" of a file without using a "namespace" or a "module" (and I’ll talk more about when/how to use namespaces and modules to organize your code in a future blog entry), then F# implicitly puts your code in a module whose name is the source code filename. So if my AnyAsString function were defined in "Debug.fs", then to call it from another file I need to write

Debug . AnyAsString [ 1 ; 2 ; 3 ]

Put another way, the original code I showed for that file is equivalent to

// Debug.fs

module Debug Debug let AnyAsString x =

sprintf "%A" x

only this version is less subtle (by virtue of being explicit about the module).

Debugging F# code – breakpoints, locals, and the immediate window

Consider this silly little program which I’ve defined in Program.fs:

// Program.fs

// demonstrate calling a function from another file

printfn "%s" ( Debug . AnyAsString [ 1 ; 2 ; 3 ] ) printfnDebugAnyAsString type Suit =

| Club

| Diamond

| Heart

| Spade type Rank =

| Ace | Deuce | Trey | Four | Five

| Six | Seven | Eight | Nine | Ten

| Jack | Queen | King



type Card =

| Card of Suit * Rank let bullet = Card(Spade,Ace)

printfn "%A" bullet let Guess(yourCard : Card) =

if yourCard = bullet then

printfn "I knew it, everyone always picks the ace of spades!"

else

printfn "Not the spade ace? You are very clever." let myCard = Card(Diamond,Trey)

printfn "(mine is %A)" myCard

Guess(myCard)

The program itself is not very interesting or meaningful, but that’s good, because I want to focus on the tooling. Suppose I put a breakpoint on the first line of the Guess function (by clicking in the margin or pressing F9):

and run the program inside the debugger. When I hit the breakpoint, in the Locals window (Debug > Windows > Locals), you’ll see the "yourCard" parameter (near the bottom):

but the value of the card (3 of diamonds) is not apparent from this view. (EDIT, Jan 2010: note that the default debugger experience here has greatly improved, see this entry.) Even expanding the "+" entry for this local variable will not help much. However, thanks to our Debug.AnyAsString function, we can easily see the value using the Immediate window (Debug > Windows > Immediate). In the Immediate window, I can type "Debug.AnyAsString(yourCard)" and it shows me the value (near the bottom right):

There are a number of improvements to the out-of-the-box F# debugging experience that we hope to make in future releases, but in the meantime, this is one alternative technique you can use to inspect the value of local variables inside the debugger.

AnyAsString is an example of a "utility function" that you may find useful in many of your F# programs. Next time I’ll discuss various ways to organize and reuse small libraries of utility code across multiple projects and solutions.