Back to Posts

Sharpening F#: Part 1 - Installation and Intro

Posted in F#, Sharpening-F#

Posts

Intro

F# is mature, open source, cross-platform, functional programming language. F# is developed by F# Software Foundation and Microsoft. F# is a meta language and is implementation of OCaml for .NET framework. You can read more about it at Wikipedia and F# Software Foundation.

Installation

There is a very comprehensive guide on the official site, so I will just post the links here.

Note: These tutorials will be presented in Visual Studio Code on Windows

Resources

You can find a lot of code snippets at F# Snippets. And if you want to try out the language without installing anything you can always go to Try F#. Also the official documentation is at F# Guide and F# Language Reference

Creating new project

Since we are going to use VS Code, I’ll try to use as musch as I can dotnet CLI.

  • First create folder where you will put you solution
  • Open PowerShell / Terminal to that location
  • The command for creating new SLN file is: dotnet new sln -n [NAME]
  • Create new console application: dotnet new console -n [NAME] -lang F#
  • Now lets add our console application to the solution: dotnet sln [NAME].sln add [PATH/TO/APP].fsproj
  • Let’s build the project: dotnet build
  • Start the console application with: dotnet run --project [NAME]

Usually F# programs don’t have entry point which means everything is executed from top to bottom, but in case of console applications like this there is special annotation that marks function as entry point [<EntryPoint>]. The created project structure should look something like this Project structure and the content of Program.fs will be Project structure

Data types in F#

FSharp Type .NET Type Example
sbyte System.SByte 4y
byte System.Byte 6uy
int16 System.Int16 10s
uint16 System.UInt16 80us
int System.Int32 325
uint32 System.UInt32 325u
int64 System.Int64 9874L
uint64 System.UInt64 98741UL
float, double System.Double 2.01
float32, single System.Single 2.0f
nativeint System.IntPtr 324n
unativeint System.UIntPtr 324un
decimal System.Decimal 324m
bigint Microsoft.FSharp.Math.BigInt 123I
char System.Char ‘a’
string System.String “abc”
bool System.Bool true

F# has very strong type inference mechanism, which means in most cases the compiler will know the type. Let see this in few examples

let varSbyte = 4y		// sbyte
let varByte = 6uy		// byte

let varInt16 = 10s		// int16
let varUint64 = 98741UL		// uint64

let varNativeint = 324n		// nativeint
let varDecimal = 324m		// decimal

let varBigint = 123I		// bigint
    
let varChar = 'a'		// char
let varString = "abc"		// string

let varBool = true		// boolean

Variables

Variables in F# are immutable, once they are bound to a value, they can’t be changed. They are compiled as static read-only properties. In case you need mutable variable, then we can use mutable keyword for such variable. Declaring variables in F# is done with let keyword.

let a = 10				// a: 10
let b = 15				// b: 15
let c = a + b				// c: 25

If we need to specify type of the variable we can do it like this

let a : int32 = 10			// a: 10
let b : int32 = 15			// b: 15
let c : int32 = a + b			// c: 25

Specifying mutable variables

let mutable d = 10		// d: 10
d <- 15				// d: 15
d <- 30				// d: 30

Operators

Arithmetic Operators

let x = 20
let y = 10
let z = 5I

let add = x + y
let sub = x - y
let mul = x * y
let div = x / y
let modu = x % y
let exp = z ** y

// result: 30, 10, 200, 2, 0, 9765625

Comparison operators

let u = 20
let p = 10

let equ = u = p
let dif = u <> p
let grl = u > p
let grr = u < p
let gel = u >= p
let ger = u <= p

// result: false, true, true, false, true, false

Bolean operators

let i = true
let o = false

let band = i && o
let bor = i || o
let bnot = not (i && o)

// result: false, true, true

Bitwise operators

let s = 20
let d = 14

let binaryAdn = s &&& d
let binaryOr = s ||| d
let binaryXor = s ^^^ d
let binaryOnesComplement = ~~~ s
let binaryLeftShift = s <<< 2
let binaryRightShift = d >>> 2

// result: 4, 30, 26, -21, 80, 3

Control statements

// if - then
if s > 0 then printfn "S is larger then 0"

// if - then - else
if s = d then printfn "S == D"
else printfn "S != D"

// if - then - elif - else
if s = 0 then printfn "S == 0"
elif s > 0 then printfn "S > 0"
else printfn "S < 0"

// nested statements
if s > 0 then 
	if d < 100 then printfn "S > 0 and D < 100"
	elif d > 100 then printfn "S > 0 and D > 100"
	else printfn "S > 0 and D == 100"
else printfn "S <= 0"

Loops

for it = 0 to 15 do
	printf "%A " it
// result: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    
for it = 15 downto 0 do
	printf "%A " it
// result: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
    
for ch in "abcdefg" do // or array
	printf "%A " ch
// result: 'a' 'b' 'c' 'd' 'e' 'f' 'g'

let mutable it2 = 10
while it2 > 0 do
	printf "%A " it2
	it2 <- it2 - 1
// result: 10 9 8 7 6 5 4 3 2 1

for it = 0 to 3 do
	for it2 = 3 downto 0 do
		printf "%A - %A" it it2
// result: [0-3][0-2][0-1][0-0][1-3][1-2][1-1][1-0][2-3][2-2][2-1][2-0][3-3][3-2][3-1][3-0]

Functions

In F# functions are like data types, we need to declare them like a variable, and use them in the sam way as variables. Syntax for defining functions is

// Non-recursive function definition.
let [inline] function-name parameter-list [ : return-type ] = function-body
// Recursive function definition.
let rec function-name parameter-list = recursive-function-body
  • function-name: is the name of the function
  • parameter-list: list of parameters separated by space
  • function-body: F# expressions
  • return-type: optional return type, if not specified compiler determines the return type

Examples of functions

let multiplyBy2 x = x * 2

let res1 = multiplyBy2 8
let res2 = multiplyBy2 10
printfn "%A, %A" res1 res2 // 16, 20

let multiply x y = x * y

let res3 = multiply 2 3
let res4 = multiply 8 9
printfn "%A, %A" res3 res4 // 6, 72

// defining recursive fibonacci
let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2)

let res5 = fib 4
let res6 = fib 10
printfn "%A, %A" res5 res6 // 5, 89

The important thing to notice when writting functions in F# is the arrow notation Arrow notation we can see here: int -> int -> int this means this function takes two int varables and returns int variable.

Composing and pipelining functions

Composing means creating one function from other functions, and pipelining means transfering result from one function to another

let add5 x = x + 5
let mul10 x = x * 10
let sub3 x = x - 3

let compositeFunction = add5 >> mul10 >> sub3

let res7 = compositeFunction 10
printfn "%A" res7 // (10 + 5) * 10 - 3 = 147

// pipelining of functions
let res8 = 10 |> add5 |> sub3 |> mul10
printfn "%A" res8 // (10 + 5 - 3) * 10 = 120

Options

Options in F# are used when there may not exist a value for some variable. An variable marked with option has value of None or Some(value)

let returnsOption x = 
	if x > 0 then Some(x)
	else None

let res9 = returnsOption 10
let res10 = returnsOption -10
printfn "%A, %A" res9 res10 // Some 10, <null>

Tuples

Tuple is comma - separated values, containg one or more values.

let res11 = ("Name", 2017, 3, 2)
printfn "%A" res11 // ("Name", 2017, 3, 2)

// matching tuples
let printTuple x =
	match x with
	| (name, year, month, day) -> printfn "Name: %A, Year: %A, Month: %A, Day %A" name year month day

printTuple res11 // Name: "Name", Year: 2017, Month: 3, Day 2

Final

You can download the code from GitHub, or try to follow this post step by step.

Open on GitHub

Read Next

ASP.NET Core 2.0: Identity in Data Layer