Learning F# – Part 4

Dis­claimer: I do not claim cred­it for the code exam­ples and much of the con­tents here, these are most­ly extracts from the book by Chris Smith, Pro­gram­ming F#: A com­pre­hen­sive guide for writ­ing sim­ple code to solve com­plex prob­lems. In fact, if you’re think­ing of learn­ing F# and like what you read here, you should buy the book your­self, it’s easy to read and the author has gone go great lengths to keep things sim­ple and includ­ed a lot of code exam­ples for you to try out your­self.

Tuple

A tuple (pro­nounced “two-pull”) is an ordered col­lec­tion of data, and an easy way to group com­mon pieces of data togeth­er.

A tuple type is described by a list of the tuple’s ele­ments’ types, sep­a­rat­ed by aster­isks:

clip_image001

You can even have tuples that con­tain oth­er tuples:

clip_image002

There’s a num­ber of ways to extract val­ues from a tuple, there’s fst (first) and snd (sec­ond) func­tions if you have a two-ele­ments tuple:

clip_image003

And then there’s the let bind­ing:

clip_image004

But remem­ber, you’ll get a com­pile error if you try to extract too many or too few val­ues from a tuple.

It is pos­si­ble to pass tuples as para­me­ters to func­tions:

clip_image005

Lists

Where­as tuples group val­ues into a sin­gle enti­ty, lists allow you to link data togeth­er to form a chain. Doing so allows you to process list ele­ments in bulk using aggre­gate oper­a­tors.

You can declare a list like this:

clip_image006

Notice in the snip­pet above the emp­ty list had type ‘a list because it could be of any type, there­fore it’s gener­ic.

Unlike oth­er lan­guages, F# lists are quite restric­tive in how you access and manip­u­late them — there are only two oper­a­tions you can per­form with a list:

  1. The first is cons, rep­re­sent­ed by the :: or cons oper­a­tor. This joins an ele­ment to the front or head of a list:

clip_image007

  1. The sec­ond is append, uses the @ oper­a­tor. Append joins two lists togeth­er:

clip_image008

List ranges

Declar­ing list ele­ments as a semi­colon-delim­it­ed list quick­ly becomes tedious, espe­cial­ly for large lists. To declare a list of ordered numer­ic val­ues, use the list range syn­tax:

clip_image009

If an option­al step val­ue is pro­vid­ed, then the result is a list of val­ues in the range between two num­bers sep­a­rat­ed by the step­ping val­ue:

clip_image010

List comprehensions

List com­pre­hen­sions is a rich syn­tax that allows you to gen­er­ate lists inline with F# code. The body of the list com­pre­hen­sion will be exe­cut­ed until it ter­mi­nates, and the list will be made up of ele­ments returned via the yield key­word:

clip_image011

Almost any F# code can exist inside of list com­pre­hen­sions, includ­ing things like func­tion dec­la­ra­tions and for loops:

clip_image012

When using loops with­in list com­pre­hen­sions, you can sim­ply the code by using -> instead of do yield:

clip_image013

Here’s a more com­plex exam­ple show­ing how you can use list com­pre­hen­sion to eas­i­ly find prime num­bers:

clip_image014

List module functions

The F# library’s List mod­ule con­tains many meth­ods to help you process lists:

image

The fol­low­ing exam­ple demon­strates the List.partition func­tion, par­ti­tion­ing a list of num­bers from 1 to 15 into two new lists: one com­prised of mul­ti­ples of five and the oth­er list made up of every­thing else:

clip_image015

The trick is that List.partition returns a tuple.

Aggregate Operators

Although lists offer a way to chain togeth­er pieces of data, there real­ly isn’t any­thing spe­cial about them. The true pow­er of lists lies in aggre­gate oper­a­tors, which are a set of pow­er func­tions that are use­ful for any col­lec­tion of val­ues..

List.map

List.map is a pro­jec­tion oper­a­tion that cre­ates a new list based on a pro­vid­ed func­tion. Each ele­ment in the new list is the result of eval­u­at­ing the func­tion, it has type (‘a -> ‘b) -> ‘a list -> ‘b list

The fol­low­ing exam­ple shows the result of map­ping a square func­tion to a list of inte­gers:

clip_image016

List.map is one of the most use­ful func­tions in the F# lan­guage, it pro­vides an ele­gant way for you to trans­form data.

List.fold

Folds rep­re­sent the most pow­er­ful type of aggre­gate oper­a­tor and not sur­pris­ing­ly the most com­pli­cat­ed. When you have a list of val­ues and you want to dis­til it down to a sin­gle piece of data, you use a fold.

There are two main types of folds you can use on lists, first is List.reduce which has type (‘a -> ‘a -> ‘a) -> ‘a list -> ‘a

List.reduce iter­ates through each ele­ment of a list, build­ing up an accu­mu­la­tor val­ue, which is the sum­ma­ry of the pro­cess­ing done on the list so far. Once every list item has been processed, the final accu­mu­la­tor val­ue is returned, the accumulator’s ini­tial val­ue in List.reduce is the first ele­ment of the list.

This exam­ple demon­strates how to use List.reduce to com­ma-sep­a­rate a list of strings:

clip_image001[7]

Whilst use­ful, reduce fold forces the type of the accu­mu­la­tor to have the same type as the list. If you want to use a cus­tom accu­mu­la­tor type (e.g. reduc­ing a list of items in a shop­ping cart to a cash val­ue), you can use List.fold.

The fold func­tion takes three para­me­ters:

  1. A func­tion that when pro­vid­ed an accu­mu­la­tor and list ele­ment returns a new accu­mu­la­tor.
  2. An ini­tial accu­mu­la­tor val­ue.
  3. The list to fold over.

The return val­ue of the func­tion is the final state of the accu­mu­la­tor. The type of the fold func­tion is:

(‘acc -> ‘b -> ‘acc) -> ‘acc -> ‘b list -> ‘acc

Here’s an exam­ple of how you can use it to count the num­ber of vow­els in a string:

clip_image002[7]

Folding right-to-left

List.reduce and List.fold process the list in a left-to-right order. There are alter­na­tive func­tions List.reduceBack and List.foldBack for pro­cess­ing lists in right-to-left order.

Depends on what you are try­ing to do, pro­cess­ing a list in reverse order can have a sub­stan­tial impact on per­for­mance.

List.iter

The final aggre­gate oper­a­tor, List.iter, iter­ates through each ele­ment of the list and calls a func­tion that you pass as a para­me­ter, it has type (‘a -> unit) -> ‘a list -> unit

Because List.iter returns unit, it is pre­dom­i­nate­ly used for eval­u­at­ing the side effect of the giv­en method, mean­ing that exe­cut­ing the func­tion has some side effect oth­er than its return val­ue (e.g. printfn has the side effect of print­ing to the con­sole in addi­tion to return­ing unit):

clip_image001[9]

Option

If you want to rep­re­sent a val­ue that may or may not exist, the best way to do so is to use the option type. The option type has only two pos­si­ble val­ues: Some(‘a’) and None.

A typ­i­cal sit­u­a­tion you’ll use an option type is when you want to parse a string as an int and if the string is prop­er­ly for­mat­ted you’ll get an int, but if the string is not prop­er­ly for­mat­ted you’ll get None:

clip_image002[9]

A com­mon idiom in C# is to use null to mean the absence of a val­ue. How­ev­er, null is also used to indi­cate an unini­tial­ized val­ue, this dual­i­ty can lead to con­fu­sion and bugs. If you use the option type, there is no ques­tion what the val­ue rep­re­sents, sim­i­lar to how System.Nullable works in C#.

To retrieve the val­ue of an option, you can use Option.get.

clip_image003[7]

One thing to watch out though, is that if you call Option.get on None, an excep­tion will be thrown. To get around this, you can use Option.isSome or Option.isNone to check before the val­ue of the option type before attempt­ing to access it, sim­i­lar to System.Nullable.HasValue in C#.

Printfn

printfn comes in three main flavours: printf, printfn, and sprintf.

printf takes the input and writes it to the screen, where­as printfn writes it to the screen and adds a line con­tin­u­a­tion.

pin­rtf has for­mat­ting and check­ing built-in (e.g. printfn “%s is %d%c high” moun­tain height units), it’s also strong typed and uses F#‘s type infer­ence sys­tem so the com­pil­er will give you an error if the data doesn’t match the giv­en for­mat spec­i­fi­er.

Here’s a table of printf for­mat spec­i­fiers:

image

sprintf is used when you want the result of the print­ing as a string:

clip_image001[11]

Anatomy of an F# Program

Most oth­er lan­guages, like C#, require an explic­it pro­gram entry point, often called a main method. In F#, for sin­gle-file appli­ca­tions, the con­tents of the code file are exe­cute from top to bot­tom in order with­out the need for declar­ing a spe­cif­ic main method.

For mul­ti-file projects, how­ev­er, code needs to be divid­ed into orga­ni­za­tion units called mod­ules or name­spaces.

Modules

By default, F# puts all your code into an anony­mous mod­ule with the same name as the code file with the first let­ter cap­i­tal­ized. So if you have a val­ue named value1, and your code is in file1.fs, you can refer to it by using the ful­ly qual­i­fied path: File1.value1.

You can explic­it­ly name your code’s mod­ule by using the mod­ule key­word at the top of a code file:

clip_image001[13]

Files can con­tain nest­ed mod­ules as well. To declare a nest­ed mod­ule, use the mod­ule key­word fol­lowed by the name of your mod­ule and an equals sign =. Nest­ed mod­ules must be indent­ed to be dis­am­biguat­ed from the “top-lev­el” mod­ule:

clip_image002[11]

Namespaces

The alter­na­tive to mod­ules is name­spaces. Name­spaces are a unit of orga­niz­ing code just like mod­ules with the only dif­fer­ence being that name­spaces can­not obtain val­ue, only type dec­la­ra­tions.

Also, name­spaces can­not be nest­ed in the same way that mod­ules can, instead, you can add mul­ti­ple name­spaces to the same file:

clip_image001[15]

It may seem strange to have both name­spaces and mod­ules in F#. Mod­ules are opti­mized for rapid pro­to­typ­ing and quick­ly explor­ing a solu­tion, as you have seen so far. Name­spaces, on the oth­er hand, are geared toward larg­er-scale projects with an object-ori­ent­ed solu­tion.

Program Startup

For sin­gle file projects, the code will be exe­cut­ed from top to bot­tom, how­ev­er, when a you add a new file to the project, the new­ly added file will be run when the pro­gram starts up.

For more for­mal pro­gram-start­up seman­tics, you can use the [<Entry­Point>] attribute to define a main method. To qual­i­fy, your method must:

  • Be the last func­tion defined in the last com­piled file in your project.
  • Take a sin­gle para­me­ter of type string array, which are the argu­ments to your pro­gram.
  • Return an inte­ger, which is your program’s exit code.