Throughout this series, I will be exploring the F# (pronounced F Sharp) language as a beginner. Perhaps you're just like me in that you've never worked with the F# language before but you are very curious about it. You may not understand the hype you've been hearing about so-called functional languages. But that's OK. If you want to learn along with me, that would be great.

Along the way, I welcome your comments and feedback, both to instruct me and other readers. You can get an overview of the complete series by visiting the series index. Enjoy.

 

Part 3 - Functional Programming

An Introduction to Functional Programming with F#

Many years ago, when a new programming language appeared, the few programmers who existed would, on average, spend a few days or a weeks learning the syntax and the concepts of the language. We invested that time because we were looking for ways to fill in the gaps left by our 9-to-5 languages. And there were many gaps to fill. I started out my programming career using C Language and a machine language assembler. We wrote to the metal and achieved truly amazing performance in our applications. But we had to build almost everything from scratch. As a result, we spent way too much time working on the fundamentals and way too little time adding tangible value to the businesses that we served.

But in recent times, languages like C# and Java have become so rich that most developers don't feel a need for spending a lot of their spare time understanding new things that are vastly different from that which they already know. While we may spend time learning new techniques, those new skills are often aimed at increasing the efficiency or reducing the complexity with which we program in our favorite language. Putting some rough numbers in place, I'd say that when I was a C Language developer, my language did 65 percent of what I wanted it to do. C++ gave me about 75 percent satisfaction and Java approached 85 percent. C# is so rich, however, that I believe I'm achieving 95 percent satisfaction in using it. But there are still some things that are hard to do in C#.

Over the past couple of years, I've been using the Python language to help in those cases where C# is just too statically typed. In .NET, I can inject Python code directly into C# using the Dynamic Language Runtime to get an interesting set of capabilities. Mixing Python into C# can give C# a kind of fluidity that cannot be achieved on it's own. But dynamically-typed, duck-typed languages like Python also have their problems. Sometimes they are just too weakly typed to be valuable in a large-scale commercial deployment. For larger applications, I rely on the safety of the static typing that C# provides. In searching for a language that would have the compile-time type safety of C# with the type inference capabilities of Python, I found F#.

My first stumbling block in learning F# was that in looking at the code, I couldn't tell what was going on. That's a huge barrier in learning any new language. Based on the richness of my favorite language as described earlier, if I can't figure out the syntax of a new language within ten minutes, I will probably stop trying at that point because with a little bit more effort, I can probably make C# do what I want. When I look at Visual Basic or Ruby code, I can generally tell what's going on despite the fact that I am not proficient in either of those languages. But F# really threw me for a loop. As I stared at F# code for the first time, I struggled to find some prototype in my brain deposited during my 25 year-long programming career that I could draw upon to make sense of this strange syntax. But I must admit, it just wasn't there. I did not possess the tools to discern the logic buried in the F# code I was looking at.

This was discouraging to me because my friends Amanda Laucher and Matt Podwysocki had told me so many great things about F# and I wanted to share their enthusiasm. But I asked myself, "If F# is that odd, should I step so far out of my comfort zone to learn it?" I almost gave up. Then my friend Justin Etheredge presented a talk on the Functional Programming Features in C# to the Richmond .NET User Group. In that talk, I recognized features that had crept into my favorite language and how they related to the fundamentals of F# which I had read about but didn't yet fully understand. I saw an opportunity to write better C# by learning F#. And that was enough to convince me that I needed to buckle down and learn F# once and for all.

Diving right into the F# syntax, let's take a look at some simple F# code. In this example, I am going to create an array of roman numerals and print them out to the console.

let romanize n =
    let numerals = ["nulla"; "i"; "ii"; "iii"; "iv"] in
    List.nth numerals n;;

let showArray =
    Array.iteri (fun ndx value -> printfn "%d : %s" ndx value);;

let i = Array.init 5 (fun value -> romanize value);;

showArray i;;

If you have the F# compiler and tools installed, you can simply paste this code into the F# Interactive tool (FSI). Since I am using version 1.9.4.19 of F#, I can change the directory to "C:\Program Files\FSharp-1.9.4.19\bin" in PowerShell or CMD.EXE and execute ".\FSI.exe". If you are using a different version of the F# distribution, your F# tools may be in a different location.

I am now going to walk through this sample step by step, pasting each part of the F# program into the FSI window. In this next graphic, I'll show what happens if you paste the first sample function named romanize into FSI.

Romanize1

Notice that when you paste the romanize function into FSI, it evaluates the code and outputs:

val romanize : int -> string

F# has essentially inferred the type of the parameter n as an integer and the output type of the romanize function as a string. If the F# compiler could talk through it's line of reasoning here, it might say something like, "I see how you are passing the parameter n to the List.nth function as its second parameter. So n must be an integer because that's the type expected by the nth function in the second parameter position. And by selecting the nth string from a list of strings as the last operation, the romanize function must return a string as its result type."

If you haven't already noticed it, the really interesting thing that's happening here is that although we didn't specify parameter type or return type for the romanize function, F# figured it out. And it did so at the time of compilation. So, we can already see that F# has some of the syntactical beauty of Python, Ruby or JavaScript combined with the type safety of C# or Java. Let's paste the next part of the F# program shown above into FSI.

Romanize2

After pasting the showArray function into FSI, we can see F#'s interpretation. However, in this case, we run into a type name that most of us will not recognize: unit. So, this function accepts a string parameter and returns a unit? What's a unit? None of the programming languages I use has a type by that name. And where's the parameter to the function that F# inferred to be a string? It's missing! Well, look at the implementation of the function as I walk you through its declaration step-by-step.

First of all, how did F# figure out that I was going to pass in a string array to the showArray function? Unlike the romanize function shown above, I didn't put the name of a parameter into the declaration of the function. As you may recall, the romanize function used a parameter named n in its declaration. But here, in the showArray function, there's no parameter shown at all. But, somehow F# figured out what was going on. How? Let me show you another way that you could have written the showArray function.

>let showArray a =
-    Array.iteri (fun ndx value -> printfn "%d : %s" ndx value) a;;

val showArray : string array -> unit

In this case, I've been a bit more explicit. But the output is (almost) the same. So, the original mystery still remains. How did F# figure out that the parameter, implicitly or explicitly defined, should be a string array? Well, as a test, check out this slightly modified method I'll call showArray2. Pay careful attention to the two differences you'll see (not counting the different function name).

> let showArray2 =
-    Array.iteri (fun ndx value -> printfn "%d : %d" ndx value);;

val showArray2 : (int array -> unit)

Going top to bottom, change one that you should have noticed is that the printfn function is using a different format string of "%d : %d" instead of "%d : %s" as it did in the original showArray function. Change two is that in this case, F# has determined that the input parameter to the function should be of type int array instead of string array. You can see this by the fact that it outputs (int array -> unit) to the console.

So, how did changing the format string for the printing function steer the F# interpreter to this conclusion? C Language programmers may recognize %s to mean string when formatting output and %d to mean integer. And that's what these symbols mean here when used with printfn in F#, too. OK, let's try an experiment. If F# is really parsing the format string to infer the type of the input to this function, let's feed it something invalid and see what happens. In C, C++ or C# which are all strongly-typed languages, an invalid format string wouldn't be caught until runtime. But if our hunch is correct, F# will see the problem much sooner. Let's define a new function called showArray3.

> let showArray3 =
-    Array.iteri (fun ndx value -> printfn "%d : %p" ndx value);;

Array.iteri (fun ndx value -> printfn "%d : %p" ndx value);;
------------------------------------------^^^^^^^^^^

stdin(20,42): error FS0191: bad format specifier: '%p'. stopped due to error

Wow! F# actually does validate the format string and it must also be using the information it finds there to infer the types of other values. It saw the invalid %p format specifier and failed to compile it. That's pretty amazing to me! We've figured out how F# determined that strings would be input, but we still don't know how it knew that an array of strings (not just one string) would be required as a parameter. To understand that, we need to break down the syntax of the showArray function a bit more.

Notice that the function being invoked inside showArray is called Array.iteri. The Array class, which is fully qualified by the name Microsoft.FSharp.Collections.Array, is a helper class that contains interesting functions like the iteri function being invoked here. The iteri function iterates over the elements in an array, executing a function on each one. That function takes two parameters: the zero-based index of the array element and the value of the array element at that index. That function that is invoked for each element has to return nothing. In languages like C++ or C#, we would call a function that returned nothing void. In Visual Basic, you would call it a Subroutined instead of a function. But F# uses the term unit for this concept instead. The unit type is sometimes shown as empty parentheses () in F# interpreter output.I like to think of the F# term unit as meaning the function performs some unit of work that may produce a side effect but has no real return value. If you were to paste a method into FSI matching the functional signature of the Array.iteri function, you would see F#'s interpretation of the method like this:

val iteri: (int -> 'a -> unit) -> 'a array -> unit

The 'a syntax is F#'s way of saying "a generic type". So reading this in English, I might say, "The iteri function takes two parameters. The first parameter is a function. See the parentheses I put around that parameter definition to help you understand that? That function parameter take two parameters of its own. The first is of type int and second is one of whatever generic type the array I'm iterating over contains. That function to be passed as a parameter returns nothing, otherwise called unit. The second parameter to the iteri function is the array itself. And, finally, the iteri function returns nothing."

Now go back and look at the first parameter passed to Array.iteri in the showArray function. It reads:

(fun ndx value -> printfn "%d : %s" ndx value)

This maps to the (int -> 'a -> unit) portion of the Array.iteri signature shown above. The F# keyword fun means this is an inline function. The two parameter are ndx which is an integer and value which is a something, a generic thing held in an element of the array. F# needs to figure out what type that something is. In this case, since we are invoking printfn to print a string to standard output, the only way to determine what type value is, is to look at the format string passed to printfn. Since the value parameter maps to the specifier %s in the format string, the type of value must be a string. And since we are iterating over an array using Array.iter, the entire array must contain strings as a result. This is how F# figured out that the showArray method must accept a string array parameter.

Wow! I don't know about you but that makes my head spin. Forget for a moment that in this language called F#, I can pass functions around as parameters. That's not too big of a stretch if you've used function pointers in C Language or delegates/anonymous methods/lambda expressions in C#. But the depth of probing being done by the F# compiler for type inference is way more than most of us are accustomed to, including me. I like it though. The fact that the compiler is doing that much work for me means that I can write far less code yet have all of the type safety to which I'm accustomed in languages like C#.

Let's take the next step in the program started above by pasting the next line of F# code into FSI.

Romanize3

With this line of F# code, we're using another of the helper functions in the Microsoft.FSharp.Collections.Array class called init. If you could paste the function for init into FSI, it would interpret it like this:

int -> (int -> 'a) -> 'a array

Let's see if we can use the F# skills we've developed so far to read this in English. Array.init must take two parameters: an integer and a function. And it must return an array of some generic, unnamed type. The second parameter, the function one, takes an integer and returns a single instance of the generic type that the array that will be returned contains. So, matching up the call to Array.init in our sample code, the number 5 must be the first integer parameter. That essentially says "Create a one-dimensional array of length 5." And as we saw before in the showArray function, the F# keyword fun is being used here again when invoking Array.init to pass a function as the second parameter to it. This is standard F# faire. Passing functions to other functions is the F# way of doing things.

So, using the signature for Array.iter above, let's analyze what's actually passed as the second function parameter.

(fun value -> romanize value)

If this function must accept an integer, then the value parameter must be an integer. And since it must return a single instance of the type contained in the array, then the call to romanize value must return one of those. Going back to our romanize function definition, we can see that it accepts an integer and returns a string. The value parameter given here is an integer. That matches which is apropos. And the return type of the romanize function is used by Array.init to infer that the array it should create consists entirely of strings. Then the F# interpreter has aptly discerned the type of the value i in our small program as a string array. Lastly, let's execute the showArray function passing the string array i as a parameter.

Romanize4

The showArray method, accepting the value i as an array of strings, iterates over them invoking the printfn function to print the index and the value at that index within the array to the console.

You've learned a lot today, I hope. In the remaining 4 parts in this series, we'll ease deeper into the functional aspects of F# as we examine other language features. That's enough for now though. In closing let me show you how our little program would look if we used the #light;; directive. This directive helps clean up the F# syntax a bit depending on white space indentation to determine functional boundaries. As a part-time Python developer, I just love that feature of F#. White space should be significant, despite what my Ruby-coding friends say. Here's the light version. As an exercise, compare it to the one at the beginning to see what's changed.

#light

let romanize n =
    let numerals = ["nulla"; "i"; "ii"; "iii"; "iv"]
    List.nth numerals n

let showArray =
    Array.iteri (fun ndx value -> printfn "%d : %s" ndx value)

let i = Array.init 5 (fun value -> romanize value)

showArray i;;

That's all for this installment of my F# tutorial. Feel free to take a look at the other parts of this series exploring the F# language by visiting the series index.