14 excellent reasons to use F#

F# makes it easy to write concise code to solve complex problems on all the major desktop and mobile platforms, primarily using functional programming

14 excellent reasons to use F#
Thinkstock

F# is a strongly typed, functional-first programming language that lets you solve complex problems by writing simple code. Based on ML and built on the .NET Framework, F# offers good interoperability, portability, and run-time speed, as well as the “Five Cs”—conciseness, convenience, correctness, concurrency, and completeness.

F# was initially available only on Windows, as a Microsoft Research project, but it’s now a first-class language on a number of platforms. You can use F# on the Mac and Linux with tool support in Xamarin Studio, MonoDevelop, Emacs, and others; on Windows with Visual Studio, Xamarin Studio, and Emacs; and on Android and iOS devices and on the Web using HTML5. In addition to general purpose programming, F# is applicable to GPU code, big data, games, and much more.

Why use F#? Let me give you 14 reasons.

F# is interactive

One of the advantages of F# is that it has an interactive REPL (read, evaluate, print, loop) where you can try out code, as shown in the screen image below. Clockwise, from the top left, we are seeing F# Interactive windows from Visual Studio in Windows, from TryFSharp running in Chrome, and from Xamarin Studio running on Mac OS X. The ;; tells F# Interactive to evaluate what you’ve typed; on TryFSharp the “run” button sends the same signal. Using a REPL to compile and test code before it goes into a full program both speeds up development and reduces bugs.

f repl IDG

The F# REPL. 

F# is for scripting

F# can be used as a scripting language as well as a programming language. Below we see a Visual Studio sample in which an F# script loads four F# program files and opens two .NET libraries before executing its own code. The notation [|…|] used here declares an array. The notation |> is a forward pipe, which passes the result of the left side to the function on the right side. The new lines here are not syntactically significant. They just make the code easier to read than having entire pipe expressions on a single line.

f scripting IDG

F# is for scripting. 

F# is functional

F# supports functional programming constructs such as treating functions as values, using unnamed functions in expressions, composition of functions to form new functions, curried functions, and the implicit definition of functions by way of the partial application of function arguments. In the upper screen shot below, we define and use an add function. The body of the function is indented (like Python) and the argument types are inferred as integers because of the + operator. In the lower screen shot, we supply a type annotation after the argument name using a colon and a type name, so F# knows that phrase is a string type.

f function definitions IDG

F# function definitions. 

F# is concise

The code below is a Quicksort-like algorithm implemented in F# (by Scott Wlaschin). The rec keyword indicates that the function is recursive. The match..with syntax is a switch statement on steroids, with | indicating cases. The [] indicates an empty list. The firstElem and otherElements are created automatically.

Note that there are no type declarations mentioned anywhere in the code, meaning that the function can sort lists containing any type that supports comparison operators. The fun keyword is for defining an anonymous lambda function.

let rec quicksort list =
   match list with
   | [] ->                            // If the list is empty
        []                            // return an empty list
   | firstElem::otherElements ->      // If the list is not empty 
        let smallerElements =         // extract the smaller ones
            otherElements            
            |> List.filter (fun e -> e < firstElem)
            |> quicksort              // and sort them
        let largerElements =          // extract the large ones
            otherElements
            |> List.filter (fun e -> e >= firstElem)
            |> quicksort              // and sort them
        // Combine the 3 parts into a new list and return it
        List.concat [smallerElements; [firstElem]; largerElements]

//test
printfn "%A" (quicksort [1;5;23;18;9;1;3])

For comparison, take a look at the traditional C# implementation below.

public class QuickSortHelper
{
   public static List<T> QuickSort<T>(List<T> values)
      where T : IComparable
   {
      if (values.Count == 0)
      {
         return new List<T>();
      }

      //get the first element
      T firstElement = values[0];

      //get the smaller and larger elements
      var smallerElements = new List<T>();
      var largerElements = new List<T>();
      for (int i = 1; i < values.Count; i++)  // i starts at 1
      {                                       // not 0!
         var elem = values[i];
         if (elem.CompareTo(firstElement) < 0)
         {
            smallerElements.Add(elem);
         }
         else
         {
            largerElements.Add(elem);
         }
      }

      //return the result
      var result = new List<T>();
      result.AddRange(QuickSort(smallerElements.ToList()));
      result.Add(firstElement);
      result.AddRange(QuickSort(largerElements.ToList()));
      return result;
   }
}

You’ll notice how much extra cruft the C# code has compared to the F# code. 

F# is really concise

According to Scott Wlaschin, the version of quicksort shown below—all four lines of it—has the typical concise look of F# written by an experienced functional coder. Of course, he’d be the first to point out that it doesn’t sort in place. It took me multiple readings to make sense of the code, but it was worth the time.

let rec quicksort2 = function
   | [] -> []                        
   | first::rest ->
        let smaller,larger = List.partition ((>=) first) rest
        List.concat [quicksort2 smaller; [first]; quicksort2 larger]

// test code 
printfn "%A" (quicksort2 [1;5;23;18;9;1;3])

Briefly, the first case returns an empty list if passed one, providing an exit criterion; the second case splits the list into the first element and the rest, assigning the sublist starting with the smaller value to smaller and the other sublist to larger. Within the concatenation of the sublists, the function recursively sorts the smaller and larger lists.

F# reduces bugs through strong typing

Unlike JavaScript, Ruby, and Python, F# is strongly typed, not dynamically typed. Unlike C and C++, which are also strongly typed, but require all types to be declared, F# performs type inference whenever possible. When type inference is not possible, but the type needs to be known, the F# compiler will throw an error and suggest that you supply a type annotation, as we had to do in an earlier example for the (phrase:string) argument to the toHackerTalk function. Catching a type mismatch at compile time eliminates a whole class of run-time errors to which dynamically typed languages are prone.

f strongly typed with inference IDG

F# is strongly typed with inference. 

By the way, F# let bindings are immutable unless you specifically declare them mutable.

F# has a large, well-chosen set of objects, including List, String, and Array

As you can see from the IntelliSense below, F# has rich List, String, and Array modules based on the .NET Framework. In this respect, it is also an object-oriented language, even though it is first and foremost a functional language. Notice that it doesn’t matter whether you use the module name or a typed variable name—when you add the dot, the member functions will pop up. Some people argue that explicitly using the module name is a better style for a functional language than dotted variables, but I don’t completely buy that argument.

f string list array IDG

F#’s String, List, and Array module functions. 

F# is useful for MapReduce

MapReduce is an efficient two-step process often used on big data, and explicitly supported in Hadoop. In this F# example, we are mapping and reducing a list of integers. First we filter the list to the even numbers, then we double each number, and finally we take the sum of all the elements in the list to aggregate or reduce the result. List.map is a powerful higher-order function; a higher-order function is one that takes another function as an argument. In addition to lists and arrays, F# supports records, sequences, data type providers, and LINQ (language-integrated query).

f mapreduce on list IDG

F# mapreduce on a List. 

F# has records

F# records represent simple aggregates of named values, optionally with members. In the example below, first we define a Book record type with four named values, and then we create a record using the same four names. The F# compiler correctly infers the Book type by matching the names.

f book records IDG

An F# record. 

F# records can have optional values

Records don’t always have to include all of their named values. If you give a named value the option attribute when you define the type, then it can be left out of a record. When you set an optional value, it can either be None, which winds up as a null, or it can be Some followed by the value you want to set. Record fields differ from classes in that they are automatically exposed as properties. Classes and structures in F# are .NET classes and structures, compatible with C# and Visual Basic .NET, so I’ll forgo examples.

f book record option attribute IDG

An F# record with an optional attribute. 

F# has sequences

A sequence in F# is a logical series of elements all of one type. Sequences are particularly useful when you have a large, ordered collection of data but do not necessarily expect to use all the elements. Individual sequence elements are computed only as required, so a sequence can provide better performance than a list in situations in which not all the elements are used. The Seq module provides support for manipulations involving sequences. In the image below, we demonstrate simple sequences, sequences with expressions, and sequences with filters.

f sequences IDG

F# sequences. 

F# supports data providers and LINQ

Below we are using the TryFSharp editor to open an online Freebase meteorology data set and query the data provider for cyclones that have recorded the highest wind values. The query { } syntax implements LINQ for F#. Use of this DLL is specific to TryFSharp. In Visual Studio, you would open Microsoft.FSharp.Data.TypeProviders and then use the appropriate data provider service.

f cyclones data provider IDG

Querying a data provider in F#. 

The result:

 [Hurricane Andrew; Hurricane Hugo; 1900 Galveston hurricane;
   Tropical Storm Allison; Cyclone Tracy; Hurricane Iniki; Hurricane Ivan;
   1999 Odisha cyclone; Hurricane Katrina; Typhoon Talim; Hurricane Rita;
   Typhoon Herb; Hurricane Wilma; Typhoon Vera; 1962 Pacific typhoon season;
   Typhoon Ike; Typhoon Mireille; Typhoon Babe; Tropical Storm Arlene;
   Hurricane Irene; Typhoon Zeb; Typhoon Maemi; Typhoon Bess; Typhoon Chanchu;
   Typhoon Patsy; Typhoon Ewiniar; Hurricane Ioke; Typhoon Xangsane;…

F# can analyze Hadoop data

In this example, we use the TryFsharp editor to open a Hadoop Hive instance that contains, among other data sets, measurements of iris flower features, along with units of measure annotations. Accordingly, we’ve enabled the use of unit annotations in the properties of the HiveTypeProvider.

f hadoop IDG

F# analyzes Hadoop data. 

This calculation returns:

val avgPetalLength : float<Data.UnitSystems.SI.UnitNames.metre> = 0.0374966443

F# does pattern matching

The F# match expression provides branching control that is based on the comparison of an expression with a set of patterns. Lines 1-7 of the example below define a recursive isPalindrome function. Lines 8-10 define a wrapper function for isPalindrome that calls it the first time using the entire string. Because “aba” is a palindrome, the then clause of line 9 fires and returns Some s, and the match statement in line 11 generates “The string aba is palindrome”.  The _ pattern in line 14 is the default case.

f pattern match IDG

Using the F# match statement. 

The match..| statement in F# has many benefits over the switch..case statement in C#, C++, and Java, the most important of which is that it causes fewer bugs.

F# supports asynchronous workflows

F# has access to all of the .NET Framework, but it also has its own syntax for asynchronous workflows. The async { expression } syntax defines a non-blocking computation. The do! keyword performs an asynchronous operation and waits for the result. The let! keyword waits on an asynchronous operation and assigns the result. And use! waits on an asynchronous operation, assigns the result, and releases the resource. Async.RunSynchronously executes an asynchronous operation and waits for its result. To add parallelism, use the Async.Parallel function, which takes a list of the Async objects, sets up the code for each Async task object to run in parallel, and returns an Async object that represents the parallel computation. Then pipe that result to Async.RunSynchronously. (The example below is from F# for Fun and Profit.)

f async IDG

An asynchronous operation in F#. 

F# resources

For more information on F#, follow the links below.

Copyright © 2018 IDG Communications, Inc.