F# is an open-source and cross-platform language which excels at succinct, correct and maintainable code. F# is used for data scripting, data science, web programming and component development. It interoperates with a wide range of software libraries and tools and all .NET and C# libraries can be used directly from F#. A key characteristic of F# is that you can use it from small-scale scripting and development to large-scale software delivery.
F# is well suited for literate programming using Azure and Jupyter notebooks because declarations are ordered in a script-like way. This notebook assumes you are familiar with some programming already. However, the examples are kept simple, so if you are just learning F#, that's fine too! There are lots of excellent F# learning resources available online. The F# Cheetsheet is a quick guide.
F# is supported by the F# Software Foundation and a worldwide community of contributors. Microsoft and other companies develop professional tooling for F#. The F# Language Reference is a reference for the F# language, and the F# Guide covers general topics. F# on Azure is a technical guide for using F# in conjunction with a range of Azure services.
To learn more about how to use Jupyter notebooks, see the Jupyter documentation and the Jupyter keyboard shortcuts. You can install the F# and Jupyter tooling locally using IfSharp.
Please share your notebooks with your friends and colleagues! The #fsharp tag on twitter can be used to share with the community.
Let's start with some simple arithmetic and data:
In [1]:
(12/4 + 5 + 7) * 4 - 18
Out[1]:
Here is a list of numbers:
In [2]:
let sampleNumbers = [ 0 .. 15 ]
sampleNumbers
Out[2]:
Next you use let
to define a function that accepts and returns an integer. Parentheses are optional for function arguments. When needed, annotate the type of a parameter name using (argument: type)
. Documentation comments are added using ///
.
In [3]:
/// A function to compute a sample curve
let sampleFunction (x:int) =
2*x*x - 5*x + 3
sampleFunction (7 + 4)
Out[3]:
To convert between numeric typs int
, double
, int64
, bigint
and so on, use conversion functions of the same name
In [4]:
sampleFunction (int 3.14 + int 2.79)
Out[4]:
F# uses indentation aware syntax (like Python). You can find out more about this in the topic F# syntax: indentation and verbosity.
Conditionals are expressed using if ... then ... else
and booleans are expressed using true
, false
, &&
, ||
and not
:
In [5]:
if 98.0 < 100.0 then 10 else 20
Out[5]:
In [6]:
not false && (true || false)
Out[6]:
In [7]:
let helloWorld = "Hello" + " " + "world"
helloWorld
Out[7]:
A tuple combines multiple data items into one value. Here is a tuple consisting of an integer, a string, and a double-precision floating point number
In [8]:
(1, "fred", 3.1415)
Out[8]:
Lists are linear sequences of values of the same type. Here is a list containing all the Fridays of the first half of 2017. THis also shows you how to use some of the very extensive .NET libraries, all of which are available to F#. You can find out more about the .NET libraries in online resources such as the .NET Framework API Reference.
In [9]:
open System
let fridaysList =
[ for month in 1 .. 6 do
for day in 1 .. DateTime.DaysInMonth(2017, month) do
let date = DateTime(2017, month, day)
if date.DayOfWeek = DayOfWeek.Friday then
yield date.ToShortDateString() ]
fridaysList
Out[9]:
If you edit the previous bit of code yourself, you will notice that you get autocomplete assistance while editing. This gives you IDE-like editing inside your Azure Notebook.
Arrays are similar to lists but are mutable and are stored as flat data rather than linked lists:
In [10]:
let lowNumbers = [| 1 .. 200 |]
lowNumbers
Out[10]:
Lists, arrays and sequences can be processed using functions. Use the pipeline operator |>
and a function to process data using List.map:
In [11]:
sampleNumbers
|> List.map (fun x -> x*x)
Out[11]:
Pipelines can be chained together. The following pipeline computes the sum of a selection of square numbers:
In [12]:
let sumOfSelectedSquares =
sampleNumbers
|> List.map (fun x -> x*x)
|> List.filter (fun x -> x % 3 = 0)
|> List.sumBy (fun x -> x * x)
sumOfSelectedSquares
Out[12]:
Both lists and arrays can use slicing notation:
In [13]:
lowNumbers.[0 .. 50]
Out[13]:
In [14]:
let rnd = System.Random()
let rec randomWalk x =
seq { yield x
yield! randomWalk (x + rnd.NextDouble() - 0.5) }
let firstValuesOfRandomWalk =
randomWalk 5.0
|> Seq.truncate 20
|> Seq.toList
firstValuesOfRandomWalk
Out[14]:
F# is a typed language. Here you define a record type. You can learn more about F# type definitions in online resources such as F# for Fun and Profit.
The type definition uses an option value. Option values are any kind of value tagged with either 'Some' or 'None'. They are used extensively in F# code to represent the cases where many other languages would use null references.
In [15]:
type ContactCard =
{ Name : string
Phone : string
Verified : bool
ZipCode : string option}
let sampleCard = { Name = "Alf" ; Phone = "(206) 555-0157" ; Verified = false; ZipCode=Some "90210" }
sampleCard
Out[15]:
In [16]:
let showCard (c: ContactCard) =
c.Name + " Phone: " + c.Phone + (if not c.Verified then " (unverified)" else "")
showCard sampleCard
Out[16]:
In [17]:
open Microsoft.FSharp.Data.UnitSystems.SI.UnitNames
let raceLength = 1600.0<meter>
[<Measure>]
type mile =
/// Conversion factor mile to meter: meter is defined in SI.UnitNames
static member asMeter = 1609.344<meter/mile>
/// Distance expressed using imperial units
let distanceToWalk = 500.0<mile>
// Same distanceusing metric system
let distanceToWalkInMeters = distanceToWalk * mile.asMeter
(raceLength, distanceToWalk, distanceToWalkInMeters)
In [18]:
let oneBigArray = [| 0 .. 100000 |]
// Do some CPU intensive computation
let rec computeSomeFunction x =
if x <= 2 then 1
else computeSomeFunction (x - 1) + computeSomeFunction (x - 2)
/// Do a parallel map over a large input array
let computeResults() = oneBigArray |> Array.Parallel.map (fun x -> computeSomeFunction (x % 24))
computeResults()
Out[18]:
A range of F# learning topics are available online at The F# Software Foundation
In [23]:
type MyType = { FirstName: string; LastName: string }
let records =
[|
{ FirstName = "Walter"; LastName = "Harp" }
{ FirstName = "Jeff"; LastName = "Smith" }
{ FirstName = "Ben"; LastName = "Smith" }
{ FirstName = ""; LastName = "Holly" }
|]
records |> Util.Table
Out[23]:
You can also filter the table to display a subset of the data as you display it:
In [24]:
Util.Table(records, [| "LastName" |])
Out[24]:
In [25]:
"f(x)" |> Util.Math
Out[25]:
Lovely! You have used Util.Math: string -> LatexOutput
to format LaTeX. The result of your code snippet is implictly passed to the Display
function and shown in your output. Next we format some operators:
In [26]:
"\int_0^\infty e^{-x^2} dx \mathrm{\ in\ \LaTeX{}}"
|> Util.Math
Out[26]:
The result of your code snippet is implictly passed to the Display
function and shown in your output. The Display
function is provided by IfSharp
. It takes any object and attempts to display it in your notebook. By default, Display
already handles many types (including LatexOutput
), and later we'll see how to extend it to handle any type you need.
You can also call Display
in your code, in order to display more than one output.
We can display charts, using XPlot.Plotly
:
In [27]:
#load "XPlot.Plotly.Paket.fsx"
#load "XPlot.Plotly.fsx"
open XPlot.Plotly