val example : string

Full name: Index.example
val exercise : string

Full name: Index.exercise
val value : int

Full name: Index.value
val stringValue : string

Full name: Index.stringValue
val integerValue : int

Full name: Index.integerValue
val ( I can have spaces ) : string

Full name: Index.( I can have spaces )
val helloWorld : string

Full name: Index.helloWorld
val replaced : string

Full name: Index.replaced
System.String.Replace(oldValue: string, newValue: string) : string
System.String.Replace(oldChar: char, newChar: char) : string
val substring : string

Full name: Index.substring
System.String.Substring(startIndex: int) : string
System.String.Substring(startIndex: int, length: int) : string
System.String.IndexOf(value: string) : int
System.String.IndexOf(value: char) : int
System.String.IndexOf(value: string, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int) : int
System.String.IndexOf(value: char, startIndex: int) : int
System.String.IndexOf(value: string, startIndex: int, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int, count: int) : int
System.String.IndexOf(value: char, startIndex: int, count: int) : int
System.String.IndexOf(value: string, startIndex: int, count: int, comparisonType: System.StringComparison) : int
val ( example 1.1 ) : string

Full name: Index.( example 1.1 )
System.String.ToLower() : string
System.String.ToLower(culture: System.Globalization.CultureInfo) : string
val initial : decimal

Full name: Index.initial
val afterFirstYear : int

Full name: Index.afterFirstYear
val ( exercise 1.1 ) : int

Full name: Index.( exercise 1.1 )
val range : int list

Full name: Index.range
val add : x:int -> y:int -> int

Full name: Index.add
val x : int
val y : int
val addResult : int

Full name: Index.addResult
val pipeResult : int

Full name: Index.pipeResult
val lengths : int list

Full name: Index.lengths
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val map : mapping:('T -> 'U) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.map
val s : string
property System.String.Length: int
val isOdd : number:int -> bool

Full name: Index.isOdd
val number : int
val ( example 1.2 ) : string

Full name: Index.( example 1.2 )
val filter : predicate:('T -> bool) -> list:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.filter
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
module String

from Microsoft.FSharp.Core
val concat : sep:string -> strings:seq<string> -> string

Full name: Microsoft.FSharp.Core.String.concat
val filter : (('a -> bool) -> seq<'a> -> 'a0)
val predicate : ('a -> bool)
type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool
val sequence : seq<'a>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
val isOdd : ('a -> bool)
val number : 'a
val ( exercise 1.2 ) : int

Full name: Index.( exercise 1.2 )
val writeLine : unit

Full name: Index.writeLine
namespace System
type Console =
  static member BackgroundColor : ConsoleColor with get, set
  static member Beep : unit -> unit + 1 overload
  static member BufferHeight : int with get, set
  static member BufferWidth : int with get, set
  static member CapsLock : bool
  static member Clear : unit -> unit
  static member CursorLeft : int with get, set
  static member CursorSize : int with get, set
  static member CursorTop : int with get, set
  static member CursorVisible : bool with get, set
  ...

Full name: System.Console
System.Console.WriteLine() : unit
   (+0 other overloads)
System.Console.WriteLine(value: string) : unit
   (+0 other overloads)
System.Console.WriteLine(value: obj) : unit
   (+0 other overloads)
System.Console.WriteLine(value: uint64) : unit
   (+0 other overloads)
System.Console.WriteLine(value: int64) : unit
   (+0 other overloads)
System.Console.WriteLine(value: uint32) : unit
   (+0 other overloads)
System.Console.WriteLine(value: int) : unit
   (+0 other overloads)
System.Console.WriteLine(value: float32) : unit
   (+0 other overloads)
System.Console.WriteLine(value: float) : unit
   (+0 other overloads)
System.Console.WriteLine(value: decimal) : unit
   (+0 other overloads)
val writeLineIsUnit : bool

Full name: Index.writeLineIsUnit
val day : string

Full name: Index.day
Multiple items
type DateTime =
  struct
    new : ticks:int64 -> DateTime + 10 overloads
    member Add : value:TimeSpan -> DateTime
    member AddDays : value:float -> DateTime
    member AddHours : value:float -> DateTime
    member AddMilliseconds : value:float -> DateTime
    member AddMinutes : value:float -> DateTime
    member AddMonths : months:int -> DateTime
    member AddSeconds : value:float -> DateTime
    member AddTicks : value:int64 -> DateTime
    member AddYears : value:int -> DateTime
    ...
  end

Full name: System.DateTime

--------------------
System.DateTime()
   (+0 other overloads)
System.DateTime(ticks: int64) : unit
   (+0 other overloads)
System.DateTime(ticks: int64, kind: System.DateTimeKind) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, calendar: System.Globalization.Calendar) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: System.DateTimeKind) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: System.Globalization.Calendar) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: System.DateTimeKind) : unit
   (+0 other overloads)
property System.DateTime.Now: System.DateTime
property System.DateTime.DayOfWeek: System.DayOfWeek
type DayOfWeek =
  | Sunday = 0
  | Monday = 1
  | Tuesday = 2
  | Wednesday = 3
  | Thursday = 4
  | Friday = 5
  | Saturday = 6

Full name: System.DayOfWeek
field System.DayOfWeek.Thursday = 4
val day2 : string

Full name: Index.day2
val today : System.DayOfWeek
val workDay : System.DayOfWeek option

Full name: Index.workDay
field System.DayOfWeek.Saturday = 6
field System.DayOfWeek.Sunday = 0
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val length : value:string -> int

Full name: Index.length
val value : string
val lengthOfWord : int

Full name: Index.lengthOfWord
val parseBool : value:string -> bool option

Full name: Index.parseBool
val lowercase : string
System.String.ToLowerInvariant() : string
val ( example 2.1 ) : bool option

Full name: Index.( example 2.1 )
val parseNumber : value:string -> Option<int>

Full name: Index.parseNumber
module Option

from Microsoft.FSharp.Core
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
val ( exercise 2.1 ) : Option<int>

Full name: Index.( exercise 2.1 )
val intArray : int []

Full name: Index.intArray
val rangeArray : int []

Full name: Index.rangeArray
val isPalindrome : value:string -> bool

Full name: Index.isPalindrome
val charList : char []
System.String.ToCharArray() : char []
System.String.ToCharArray(startIndex: int, length: int) : char []
val reversed : char []
module Array

from Microsoft.FSharp.Collections
val rev : array:'T [] -> 'T []

Full name: Microsoft.FSharp.Collections.Array.rev
val ( example 2.2 ) : bool

Full name: Index.( example 2.2 )
val splitBy : separator:char -> str:string -> string list

Full name: Index.splitBy
val separator : char
Multiple items
val char : value:'T -> char (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.char

--------------------
type char = System.Char

Full name: Microsoft.FSharp.Core.char
val str : string
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
val ( exercise 2.2 ) : string list

Full name: Index.( exercise 2.2 )
type Size =
  | Small
  | Medium
  | Large

Full name: Index.Size
union case Size.Small: Size
union case Size.Medium: Size
union case Size.Large: Size
type Shape =
  | Square of edge: float
  | Rectangle of width: float * height: float
  | Circle of radius: float

Full name: Index.Shape
union case Shape.Square: edge: float -> Shape
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = System.Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
union case Shape.Rectangle: width: float * height: float -> Shape
union case Shape.Circle: radius: float -> Shape
type Result =
  | Success
  | ErrorMessage of string

Full name: Index.Result
union case Result.Success: Result
union case Result.ErrorMessage: string -> Result
type FruitType =
  | Banana
  | Apple
  | Grapefruit

Full name: Index.FruitType
union case FruitType.Banana: FruitType
union case FruitType.Apple: FruitType
union case FruitType.Grapefruit: FruitType
type Meal =
  | Fruit of FruitType
  | Sandwich
  | FastFood of string

Full name: Index.Meal
union case Meal.Fruit: FruitType -> Meal
union case Meal.Sandwich: Meal
union case Meal.FastFood: string -> Meal
val ( example 3.1 ) : Meal list

Full name: Index.( example 3.1 )
type Operator = | Int

Full name: Index.Operator
union case Operator.Int: Operator
type Symbol = | Int

Full name: Index.Symbol
union case Symbol.Int: Symbol
val formatOptionalValue : optionalValue:string option -> string

Full name: Index.formatOptionalValue
val optionalValue : string option
val formattedValues : string list

Full name: Index.formattedValues
val area : shape:Shape -> float

Full name: Index.area
val shape : Shape
val edge : float
val width : float
val height : float
val radius : float
type Math =
  static val PI : float
  static val E : float
  static member Abs : value:sbyte -> sbyte + 6 overloads
  static member Acos : d:float -> float
  static member Asin : d:float -> float
  static member Atan : d:float -> float
  static member Atan2 : y:float * x:float -> float
  static member BigMul : a:int * b:int -> int64
  static member Ceiling : d:decimal -> decimal + 1 overload
  static member Cos : d:float -> float
  ...

Full name: System.Math
field System.Math.PI = 3.14159265359
val ( example 3.2 ) : float

Full name: Index.( example 3.2 )
val apply : operator:Operator -> left:int -> right:int -> int

Full name: Index.apply
val operator : Operator
val left : int
val right : int
val ( exercise 3.2 ) : int

Full name: Index.( exercise 3.2 )
val patternMatchString : value:string -> string

Full name: Index.patternMatchString
val x : string
val matchAnimal : string

Full name: Index.matchAnimal
val matchFloatingPoint : value:string -> float

Full name: Index.matchFloatingPoint
val y : string
val isHealthy : meal:Meal -> bool

Full name: Index.isHealthy
val meal : Meal
val restaurant : string
val ( example 3.3 ) : bool

Full name: Index.( example 3.3 )
val parseSymbol : token:string -> Option<Symbol>

Full name: Index.parseSymbol
val token : string
val ( exercise 3.3 ) : Option<Symbol> list

Full name: Index.( exercise 3.3 )
val sequence : optionals:'a option list -> 'a list option

Full name: Index.sequence
val optionals : 'a option list
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
val h : 'a
val t : 'a option list
val map : mapping:('T -> 'U) -> option:'T option -> 'U option

Full name: Microsoft.FSharp.Core.Option.map
val t : 'a list
val parseSymbols : expression:string -> Option<Symbol list>

Full name: Index.parseSymbols
val expression : string
val ( exercise 3.4 ) : Option<Symbol list>

Full name: Index.( exercise 3.4 )
val countdown : counter:int -> string

Full name: Index.countdown
val counter : int
System.Int32.ToString() : string
System.Int32.ToString(provider: System.IFormatProvider) : string
System.Int32.ToString(format: string) : string
System.Int32.ToString(format: string, provider: System.IFormatProvider) : string
val counting : string

Full name: Index.counting
val countdownAcc : acc:string -> counter:int -> string

Full name: Index.countdownAcc
val acc : string
val countingAcc : string

Full name: Index.countingAcc
val factorial : x:int -> int

Full name: Index.factorial
val factorialOf5 : int

Full name: Index.factorialOf5
val factorialTail : acc:int -> x:int -> int

Full name: Index.factorialTail
val acc : int
val factorialOf5Tail : int

Full name: Index.factorialOf5Tail
val commaSeparated : acc:string -> list:string list -> string

Full name: Index.commaSeparated
Multiple items
val list : string list

--------------------
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
Multiple items
val single : string

--------------------
type single = System.Single

Full name: Microsoft.FSharp.Core.single
val head : string
val tail : string list
val csv : string

Full name: Index.csv
val formatOptionalInts : acc:string -> ints:int option list -> string

Full name: Index.formatOptionalInts
val ints : int option list
val rest : int option list
val optionalInts : string

Full name: Index.optionalInts
val partitionEvenOdd : even:int list -> odd:int list -> numbers:int list -> int list * int list

Full name: Index.partitionEvenOdd
val even : int list
val odd : int list
val numbers : int list
val h : int
val tail : int list
val ( example 4.1 ) : int list * int list

Full name: Index.( example 4.1 )
val compute : stack:int list -> symbols:Symbol list -> Option<int>

Full name: Index.compute
val stack : int list
val symbols : Symbol list
val ( exercise 4.1 ) : Option<int>

Full name: Index.( exercise 4.1 )
val onp : expression:string -> Option<int>

Full name: Index.onp
val ( exercise 4.2 ) : Option<int>

Full name: Index.( exercise 4.2 )

F# CAMP

Basic concepts of Functional Programming

1: 
git clone https://github.com/theimowski/fsharp-workshops-basics.git

or download ZIP from here, then in Command Prompt:

1: 
2: 
cd fsharp-workshops-basics
.\build.cmd KeepRunning

slides are regenerated when the script (.\slides\index.fsx) is saved

Agenda

  • Intro - workshop format
  • Immutable values
  • Expressions
  • Pattern matching
  • Recursion

Workshop format

Problem to solve

Evaluate expression written in ONP notation

Conventional notation:

1: 
((2+7)/3+(14-3)*4)+3

ONP notation:

1: 
2 7 + 3 / 14 3 - 4 * + 3 +

Parse input ==> Evaluate

New Stuff X.X

Something new

1: 
// code

Example X.X

Some example

1: 
let example = "example"

Value of example

"example"

Exercise X.X

Exercise:

--------------- Your code goes below ---------------

1: 
let exercise = "exercise"

Value of exercise

"exercise"

Editors + tooling - demo

  • Visual Studio with F# + Visual F# Power Tools
  • Visual Studio Code + Ionide extension

http://fsharp.org/use/windows/

  • Running code in FSI (FSharp Interactive)
  • Regenerating the slides
  • DEMO: running the code (up to current point) in VS and VS Code

Summary/links go at the end of each part

Most links point to Scott Wlaschin blog

Immutable values

New Stuff 1.1

Let bindings

1: 
let value = 5

Type inference

1: 
2: 
let stringValue = "Hi there"
let integerValue = 100

Backticks names

1: 
let ``I can have spaces`` = "I really do"

Example 1.1

Processing an immutable value

System.String is an example of immutable type in .NET

1: 
2: 
3: 
4: 
let helloWorld = "Hello World from F# program!"
let replaced = helloWorld.Replace("o", "u")
let substring = replaced.Substring(0, replaced.IndexOf("#") + 1)
let ``example 1.1`` = substring.ToLower()

Value of example 1.1

"hellu wurld frum f#"

Exercise 1.1

Compound interest: compute earnings after 3 years of depositing $1000 on a 10% annual savings account:

--------------- Your code goes below ---------------

1: 
2: 
3: 
4: 
let initial = 1000.0M
let afterFirstYear = 0
// and so on ...
let ``exercise 1.1`` = 0

Value of exercise 1.1

0

New Stuff 1.2

Range operator

1: 
let range = [28 .. 38]
[28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38]

Function declaration and application

1: 
2: 
3: 
4: 
let add x y =
    x + y

let addResult = add 5 6
11

Pipe operator

1: 
2: 
3: 
4: 
let pipeResult =
    10
    |> add 15
    |> add 6
31

map function

1: 
2: 
3: 
let lengths = 
    ["F#"; "is"; "the"; "best"]
    |> List.map (fun s -> s.Length)
[2; 2; 3; 4]

map is an equivalent of Select() in C# LINQ

Example 1.2

LINQ-like list processing

Note: a new immutable value is created in each computation step

C# version

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
let isOdd number =
    number % 2 = 1

let ``example 1.2`` = 
    [2 .. 10]
    |> List.filter isOdd
    |> List.map string
    |> String.concat ";"

Value of example 1.2

"3;5;7;9"

Pipe operator - think "Extension methods"

C#

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public static IEnumerable<'T> Where (
    this IEnumberable<'T> sequence, 
    Func<'T, bool> predicate) { ... }

public static bool IsOdd (int number) { return number % 2 == 1; }

numbers.Where (IsOdd)

F#

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let filter 
    (predicate : 'a -> bool) 
    (sequence : seq<'a>) = ...

let isOdd number = n % 2 = 1

numbers |> filter isOdd

Exercise 1.2

Define isEven function of type int -> bool and sum even numbers from 2 up to 100. Hint: Use List.sum function

1: 
2 + 4 + 6 + ... + 100

--------------- Your code goes below ---------------

1: 
let ``exercise 1.2`` = 0

Value of exercise 1.2

0

Summary: Immutable Values

  • By default values in F# are immutable
  • "Pipe" operator (|>) is a nice syntactic sugar for writing a sequence of expressions
  • Immutable values make it easy to write concurrent code (thread safety for free)

Links

Expressions

New Stuff 2.1

In F# everything is an expression

1: 
2: 
3: 
4: 
5: 
6: 
7: 
// no statements - `Console.WriteLine` returns `Unit` ()
let writeLine = System.Console.WriteLine "Hello"

// in let bindings, `=` associates symbol with value
let writeLineIsUnit : bool = 
    // but anywhere else, `=` means equality test
    writeLine = ()
true

If - then - else expression

1: 
2: 
3: 
4: 
5: 
let day =
    if System.DateTime.Now.DayOfWeek = System.DayOfWeek.Thursday then
        "thursday"
    else
        "some other day of week"
"some other day of week"

Binding values in inside scope

1: 
2: 
3: 
4: 
5: 
6: 
let day2 =
    let today = System.DateTime.Now.DayOfWeek 
    if today = System.DayOfWeek.Thursday then
        "thursday"
    else
        "some other day of week"
"some other day of week"

Option type

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let workDay =
    let today = System.DateTime.Now.DayOfWeek 
    if today <> System.DayOfWeek.Saturday &&
       today <> System.DayOfWeek.Sunday then
        Some today
    else
        None
Some Tuesday

Dot notation, Wrapper functions

1: 
2: 
3: 
4: 
let length (value : string) =
    value.Length

let lengthOfWord = length "Hello word"
10

Example 2.1

Parsing boolean value

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
let parseBool (value : string) =
    let lowercase = value.ToLowerInvariant()
    if lowercase = "true" then
        Some true
    elif lowercase = "false" then
        Some false
    else
        None

let ``example 2.1`` = parseBool "True"

Value of example 2.1

Some true

Exercise 2.1

Implement parseNumber function. You might find following functions useful: ToCharArray() (String member), Array.forall, System.Char.IsDigit, System.Int32.Parse.

--------------- Your code goes below ---------------

1: 
2: 
3: 
4: 
let parseNumber (value: string) : Option<int> =
    None

let ``exercise 2.1`` = parseNumber "42"

Value of exercise 2.1

<null>

New Stuff 2.2

Array literal

1: 
2: 
let intArray = [|2;4;5|]
let rangeArray = [|10..15|]
[|10; 11; 12; 13; 14; 15|]

Example 2.2

No return statements. Last expression is the return value

1: 
2: 
3: 
4: 
5: 
6: 
let isPalindrome (value : string) =
    let charList = value.ToCharArray()
    let reversed = value.ToCharArray() |> Array.rev
    charList = reversed // this boolean expression returns value

let ``example 2.2`` = isPalindrome "kajak"

Value of example 2.2

true

Exercise 2.2

Declare splitBy function - a wrapper function arround Split method from String object. Hints: Use Split method from String and Array.toList function to convert array to list type.

--------------- Your code goes below ---------------

1: 
2: 
3: 
4: 
5: 
6: 
let splitBy (separator : char) (str : string) : list<string> =
    []

let ``exercise 2.2`` = 
    "1,3,5,8,10" 
    |> splitBy ',' 

Value of exercise 2.2

[]

Summary: Expressions

  • There are no statements only expressions in F#
  • Therefore no need for return keywords - last expression is return value
  • Option type is the preffered way to model missing values (as opposed to null)

Links

Pattern matching

New Stuff 3.1

Discriminated Unions - Empty cases (enum style)

1: 
2: 
3: 
4: 
type Size = 
| Small
| Medium
| Large

Discriminated Unions - Complex cases

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
type Shape =
| Square of edge : float
// `*` in type declarations stands for tuples
| Rectangle of width : float * height : float
| Circle of radius : float

type Result = 
| Success                // no string needed for success state
| ErrorMessage of string // error message needed 

Example 3.1

Modelling with Discriminated Union Types

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
type FruitType =
| Banana
| Apple
| Grapefruit

type Meal = 
| Fruit of FruitType
| Sandwich
| FastFood of string

let ``example 3.1`` = 
    [Sandwich; FastFood "Bar Żuławski"; Fruit Apple]

Value of example 3.1

[Sandwich; FastFood "Bar Żuławski"; Fruit Apple]

Exercise 3.1

Define Operator and Symbol Discriminated Union Types.

Symbol should use Operator as field in one case

--------------- Your code goes below ---------------

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
// `Int` is used here only so that the code compiles. 
// Remove it and instead define proper Discriminated Union cases:
// Operator might be one of the following: Plus, Minus, Multiply or Divide
type Operator = Int

// Same as above:
// Symbol might be either a NumSymbol (with int) or OpSymbol (with Operator)
type Symbol = Int

New Stuff 3.2

Pattern matching expression

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
let formatOptionalValue optionalValue =
    match optionalValue with
    | Some value ->
        "Value: " + value
    | None ->
        "No value at all!"

let formattedValues = 
    [Some "nice string"; None]
    |> List.map formatOptionalValue
["Value: nice string"; "No value at all!"]

Example 3.2

Calculating area of Shape with help of pattern matching

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let area shape =
    match shape with
    | Square edge -> edge ** 2.0
    | Rectangle (width, height) -> width * height
    | Circle radius -> System.Math.PI * (radius ** 2.0)  

let ``example 3.2`` = area (Circle 10.0)

Value of example 3.2

314.1592654

Exercise 3.2

With help of pattern matching, implement apply function.

--------------- Your code goes below ---------------

1: 
2: 
3: 
4: 
5: 
let apply (operator : Operator) (left : int) (right : int) : int =
    0

// test the function, e.g. `apply Divide 15 4`
let ``exercise 3.2`` = 0

Value of exercise 3.2

0

New Stuff 3.3

Pattern matching strings

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let patternMatchString value =
    match value with
    | "Dog" -> "Animal"
    | "Cat" -> "Animal"
    | x     -> "Something different"

let matchAnimal = patternMatchString "Chair"
"Something different"

Nested match expressions

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
let matchFloatingPoint value =
    match value with
    | "1" -> 1.0
    | "2" -> 2.0
    | x   ->
        match x with
        | "1.0" -> 1.0
        | "2.5" -> 2.5
        | y     -> 0.0

Example 3.3

Nested match - checking if meal is healthy

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
let isHealthy meal =
    match meal with
    | Fruit _ -> true
    | Sandwich _ -> false
    | FastFood restaurant ->
        // we can further use matched `restaurant` symbol
        match restaurant with
        | "Green Way" -> true
        | _ -> false

let ``example 3.3`` = 
    FastFood "Bar Żuławski" 
    |> isHealthy

Value of example 3.3

false

Exercise 3.3

Implement parseSymbol - try parse all operators first, and then in nested match expression use parseNumber function

--------------- Your code goes below ---------------

1: 
2: 
3: 
4: 
let parseSymbol (token : string) : Option<Symbol> =
    None

let ``exercise 3.3`` = List.map parseSymbol ["+"; "/"; "12"; "uups"] 

Value of exercise 3.3

[null; null; null; null]

Helper function "sequence"

if all elements are Some values, return Some of those values otherwise if there's at least one None, return None

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
let rec sequence (optionals: list<option<'a>>) : option<list<'a>> =
    match optionals with
    | [] -> 
        Some []
    | None :: _ ->
        None
    | Some h :: t ->
        sequence t |> Option.map (fun t -> h :: t)

Exercise 3.4

Implement parseSymbols. Useful functions: List.map, sequence as well as splitBy and parseSymbol

--------------- Your code goes below ---------------

1: 
2: 
3: 
4: 
let parseSymbols (expression: string) : Option<list<Symbol>> =
    None

let ``exercise 3.4`` = "1 2 / +" |> parseSymbols

Value of exercise 3.4

<null>

Summary: Pattern Matching

  • Using Discriminated Unions is a neat way to model data
  • Pattern matching is a powerful and elegant mechanism in F# for "branching" code
  • F# compiler warns when it finds unhandled cases in pattern matching

Links

Recursion

New Stuff 4.1

Recursive functions

1: 
2: 
3: 
4: 
5: 
6: 
let rec countdown counter =
    match counter with
    | 0 -> ""
    | x -> x.ToString() + ";" + countdown (counter - 1)

let counting = countdown 10
"10;9;8;7;6;5;4;3;2;1;"

Tail-recursive functions with accumulator

1: 
2: 
3: 
4: 
5: 
6: 
let rec countdownAcc acc counter =
    match counter with
    | 0 -> acc
    | x -> countdownAcc (acc + ";" + x.ToString()) (counter - 1)
    
let countingAcc = countdownAcc "" 10
";10;9;8;7;6;5;4;3;2;1"

Factorial

1: 
2: 
3: 
4: 
5: 
6: 
let rec factorial x =
    match x with
    | 1 -> 1
    | _ -> (factorial (x-1)) * x

let factorialOf5 = factorial 5
120

Factorial - with accumulator

1: 
2: 
3: 
4: 
5: 
6: 
let rec factorialTail acc x =
    match x with 
    | 1 -> acc
    | _ -> factorialTail (x*acc) (x-1)

let factorialOf5Tail = factorialTail 1 5
120

Pattern matching lists

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let rec commaSeparated acc list =
    match list with
    | [] -> acc
    | [single] -> acc + "," + single
    | head :: tail -> commaSeparated (acc + "," + head) tail

let csv = commaSeparated "" ["some";"values";"go";"here"]
",some,values,go,here"

Pattern in pattern

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
let rec formatOptionalInts acc ints =
    match ints with
    | [] -> acc
    | Some 0 :: rest -> formatOptionalInts (acc + " Zero") rest
    | Some x :: rest -> formatOptionalInts (acc + " " + x.ToString()) rest
    | None   :: rest -> formatOptionalInts (acc + " NoValue!") rest

let optionalInts = formatOptionalInts "" [Some 28; Some 0; None]
" 28 Zero NoValue!"

Example 4.1

Recursive call with "accumulators"

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
let rec partitionEvenOdd even odd numbers =
    match numbers with
    | [] ->
        (even, odd)
    | h :: tail when h % 2 = 0 ->
        partitionEvenOdd (h :: even) odd tail
    | h :: tail when h % 2 = 1 ->
        partitionEvenOdd even (h :: odd) tail

let ``example 4.1`` = partitionEvenOdd [] [] [1..10]

Value of example 4.1

([10; 8; 6; 4; 2], [9; 7; 5; 3; 1])

Homework 4.1

Implement compute function (Wiki). Hint: :: is "right-associative"

--------------- Your code goes below ---------------

1: 
2: 
3: 
4: 
5: 
let rec compute (stack : list<int>) (symbols : list<Symbol>) : Option<int> =
    None

// test the function, e.g. `compute [] [NumSymbol 4; NumSymbol 2; OpSymbol Multiply]`
let ``exercise 4.1`` : Option<int> = None

Value of exercise 4.1

<null>

Homework 4.2

Using parseSymbols and compute, write onp function

--------------- Your code goes below ---------------

1: 
2: 
3: 
4: 
let onp (expression : string) : Option<int> = 
    None

let ``exercise 4.2`` = onp "2 7 + 3 / 14 3 - 4 * + 3 +"

Value of exercise 4.2

<null>

Summary: Recursion

  • Recursion is a preferred way of processing in Functional Programming
  • F# provides tail recursion feature for preventing Stack Overflows
  • Recursive functions combined with pattern matching result in very concise and declarative code

Links

Summary

  • Immutable values
  • Expressions
  • Pattern matching
  • Recursion

Next week

Functional Data Structures

+ Something more?