F# Training
F# Training
F# Training
  • Presentation
  • Fundamentals
    • Introduction
    • Syntax
      • Bases
      • Functions
      • Rules
      • Exceptions
    • First concepts
    • πŸ”Quiz
  • Functions
    • Signature
    • Concepts
    • Syntax
    • Standard functions
    • Operators
    • Addendum
    • πŸ”Quiz
    • πŸ“œSummary
  • Types
    • Overview
    • Tuples
    • Records
    • Unions
    • Enums
    • Anonymous records
    • Value types
    • πŸ“œRecap
    • Addendum
  • Monadic types
    • Intro
    • Option type
    • Result type
    • Smart constructor
    • πŸš€Computation expression (CE)
    • πŸš€CE theoretical basements
    • πŸ“œRecap
  • Pattern matching
    • Patterns
    • Match expression
    • Active patterns
    • πŸš€Fold function
    • πŸ“œRecap
    • πŸ•ΉοΈExercises
  • Collections
    • Overview
    • Types
    • Common functions
    • Dedicated functions
    • πŸ”Quiz
    • πŸ“œRecap
  • Asynchronous programming
    • Asynchronous workflow
    • Interop with .NET TPL
    • πŸ“œRecap
  • Module and Namespace
    • Overview
    • Namespace
    • Module
    • πŸ”Quiz
    • πŸ“œRecap
  • Object-oriented
    • Introduction
    • Members
    • Type extensions
    • Class, Struct
    • Interface
    • Object expression
    • Recommendations
Powered by GitBook
On this page
  • Expression vs Statement
  • βš–οΈ Benefits of expressions over instructions
  • Everything is an expression
  • Early exit alternatives
  • Typing, inference and ceremony
  • Type inference
  • Type inference in Cβ™―
  • Type inference in Fβ™―
  • Automatic generalization in Fβ™― inference
  • Inference vs type annotation

Was this helpful?

Edit on GitHub
  1. Fundamentals

First concepts

PreviousExceptionsNextQuiz

Last updated 1 month ago

Was this helpful?

Expression vs Statement

A statement will produce a side effect. An expression will produce a value... and a possible side effect (that we should avoid).

  • Fβ™― is a functional, expression-based language only.

  • In comparison, Cβ™― is an imperative language, based on statements, but includes more and more syntactic sugar based on expressions:

    • Ternary operator b ? x : y

    • ?? in Cβ™― 8 : label ?? "(Empty)"

    • in Cβ™― 6 and 7

    • switch expression in Cβ™― 8

βš–οΈ Benefits of expressions over instructions

  • Conciseness: less visual clutters β†’ more readable

  • Composability: composing expressions is like composing values

  • Understanding: no need to know the previous instructions to understand the current one

  • Testability: pure are easier to test

    • Predictable: same inputs mean same outputs

    • Isolated: shorter Arrange/Setup phase in tests, no need for mocks

Everything is an expression

  • A function is declared and behaves like a value

    • We can pass it as parameter or return it from another function (1)

  • The control flow building blocks are also expressions

    • if … then/else , match … with

    • for … in, for … to, while … do just return "nothing" (2)

Notes

  • (1) See 1st-class citizens, high-order functions πŸ“

  • (2) Except in collection comprehensions πŸ“

Consequences

  • No void β†’ Best replaced by the unit type πŸ“

  • No Early Exit

    • In C#, you can exit a function with return and exit a for/while loop with break.

    • In Fβ™― these keywords do not exist.

Early exit alternatives

One solution in imperative style is to use mutable variables πŸ˜•

let firstItemOrDefault defaultValue predicate (items: 't array) =
    let mutable result = None
    let mutable i = 0
    while i < items.Length && result.IsNone do
        let item = items[i]
        if predicate item then
            result <- Some item
        i <- i + 1

    result
    |> Option.defaultValue defaultValue

let test1' = firstItemOrDefault -1 (fun x -> x > 5) [| 1 |]     // -1

The most recommended and idiomatic solution in functional programming is to use a recursive function πŸ“

[<TailCall>] // Fβ™― 8 πŸ“
let rec firstOr defaultValue predicate list =
    match list with
    | [] -> defaultValue                                // πŸ‘ˆ Exit
    | x :: _ when predicate x -> x                      // πŸ‘ˆ Exit
    | _ :: rest -> firstOr defaultValue predicate rest  // πŸ‘ˆ Recursive call to continue

let test1 = firstOr -1 (fun x -> x > 5) [1]     // -1
let test2 = firstOr -1 (fun x -> x > 5) [1; 6]  // 6

Typing, inference and ceremony

The ceremony is correlated to the typing weakness

Language
Typing strength
Inference strength
Ceremony

JS

Low (dynamic typing)

Γ—

Low

Cβ™―

Medium (static nominal)

Low

High

TS

Strong (static structural + ADT)

Medium

Medium

Fβ™―

Strong (static nominal + ADT)

High

Low

Type inference

Goal: write type annotations as little as possible

  • Less code to write πŸ‘

  • Compiler ensures consistency

  • IntelliSense helps with coding and reading

Type inference in Cβ™―

  • Method parameters and return value ❌

  • Variable declaration: var o = new { Name = "John" } βœ”οΈ

  • Lambda as argument: list.Find(i => i == 5) βœ”οΈ

  • Lambda declaration: var f3 = () => 1; βœ”οΈ in C# 10 (limited)

  • Array initialisation: var a = new[] { 1, 2 }; βœ”οΈ

  • Generic classes:

    • constructor: new Tuple<int, string>(1, "a") ❌

    • static helper class: Tuple.Create(1, "a") βœ”οΈ

  • Cβ™― 9 target-typed expression StringBuilder sb = new(); βœ”οΈ

Type inference in Fβ™―

  • Able to deduce the type of variables, expressions and functions in a program without any type annotation

  • Based on both the implementation and the usage

Example:

let helper instruction source =
    if instruction = "inc" then // 1. `instruction` has the same type than `"inc"` => `string`
      source + 1                // 2. `source` has the same type than `1` => `int`
    elif instruction = "dec" then
      source - 1
    else
      source                    // 3. `return` has the same type than `source` => `int`

Automatic generalization in Fβ™― inference

If something can be inferred as generic, it will be β†’ Open to more cases πŸ₯³

// Generic value
let a = [] // 'a list

// Generic function with both parameters generic
let listOf2 x y = [x; y]
// val listOf2: x: 'a -> y: 'a -> 'a list

// Generic type constraint inference: 'a must be "comparable"
let max x y = if x > y then x else y

Generic type parameter notation

  • starts with an apostrophe ' (a.k.a. tick)

  • can be in camelCase ('a) or PascalCase ('A)

  • Cβ™― TXxx β†’ Fβ™― 'xxx or 'Xxx

Inference vs type annotation

  • Pros:

    • code terser

    • automatic generalization

  • Cons:

    • we can break code in cascade

    • inference limited:

      • an object type cannot be determine by the call to one of its members (1) β†’ exception: Record types πŸ“

      • sensible to the order of declaration (2)

(1) Example:

let helperKO instruction source =
    match instruction with
    | 'U' -> source.ToUpper()
    //       ~~~~~~~~~~~~~~~~ πŸ’₯
    // Error FS0072: Lookup on object of indeterminate type based on information prior to this program point.
    // A type annotation may be needed prior to this program point to constrain the type of the object.
    | _   -> source

let helperOk instruction (source: string) = [...]
// Type annotation needed here  : ^^^^^^

// If there is a function equivalent to the method, it will work
let info list = if list.Length = 0 then "Vide" else "..." // πŸ’₯ Error FS0072...
let info list = if List.length list = 0 then "Vide" else $"{list.Length} Γ©lΓ©ments" // πŸ‘Œ

(2) Example:

let listKo = List.sortBy (fun x -> x.Length) ["three"; "two"; "one"]
//                                 ~~~~~~~~ πŸ’₯ Error FS0072: Lookup on object of indeterminate type...

// Solution 1: reverse the order by piping the list
let listOk = ["three"; "two"; "one"] |> List.sortBy (fun x -> x.Length)

// Solution 2: use a named function  instead of a lambda
let listOk' = List.sortBy String.length ["three"; "two"; "one"]

The most questionable solution is to raise an exception πŸ’© (see )

πŸ”— by Mark Seemann

method

Null-coalescing operator
Expression-bodied members
StackOverflow
Zone of Ceremony
Hindley–Milner