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
  • Named functions
  • Functions of 2 or more parameters
  • Functions without parameter
  • Multi-line function
  • Anonymous function
  • _.Member shorthand (F♯ 8)
  • Naming convention related to functions
  • Piping
  • Pipeline: chain of pipings
  • If/then/else expression
  • Match expression

Was this helpful?

Edit on GitHub
  1. Fundamentals
  2. Syntax

Functions

Named functions

  • Declared a let binding (like a variable)

  • Naming convention: camelCase

  • No return keyword: the function always returns the last expression in its body

  • No () around all parameters, no , between parameters

  • () required around parameter with type annotation (1) or deconstruction (2)

let square x = x * x  // Function with 1 parameter
let res = square 2    // Returns 4

// (1) Parentheses required for annotations of type
let square' (x: int) : int = x * x

// (2) Brackets required when deconstructing an object
//     (here it's a single-case discriminated union šŸ“
let hotelId (HotelId value) = value

Functions of 2 or more parameters

Separate parameters and arguments with spaces:

// Function with 2 parameters
let add x y = x + y  // val add: x: int -> y: int -> int

// Call with the 2 arguments
let res = add 1 2    // val res: int = 3

Inference

The inference of the add function can be confusing: the + works for any numbers and for strings too, but add is limited to int! To get it work, we need to write let inline add ... → Related to a special kind of generics: statically resolved type parameters (SRTP) šŸ“

āš ļøļø , creates another kind of functions using tuples šŸ“

let addByPair (x, y) = x + y
// val addByPair: x: int * y: int -> int

Functions without parameter

Use () (like in C#)

let printHello () = printfn "Hello"
// val printHello: unit -> unit
printHello ();;
// Hello

let notAFunction = printfn "Hello"
// Hello
// val notAFunction: unit = ()

ā˜ļø unit means "nothing" šŸ“

Multi-line function

Indentation required, but no need for {} Can contain sub-function

let evens list =
    let isEven x =  // šŸ‘ˆ Sub-function
        x % 2 = 0   // šŸ’” `=` equality operator - No `==` operator in F♯
    List.filter isEven list
// val evens: list: int list -> int list

let res = evens [1;2;3;4;5]
// val res: int list = [2; 4]

Anonymous function

A.k.a. Lambda, arrow function

  • Syntax: fun {parameters} -> body (≠ in C♯ {parameters} ⇒ body)

  • In general, () required all around, for precedence reason

let evens' list = List.filter (fun x -> x % 2 = 0) list

_.Member shorthand (F♯ 8)

type Person = { Name: string; Age: int }

let people =
    [ { Name = "Alice"; Age = 30 }
      { Name = "Billy"; Age =  5 } ]

// Regular lambda (Shorthand not possible)
let adults = people |> List.filter (fun person -> person.Age >= 18)
// val adults: Person list = [{ Name = "Alice"; Age = 30 }]

// Member chain shorthand
let uppercaseNames = people |> List.map _.Name.ToUpperInvariant() // šŸ‘ˆšŸ‘ˆ
// val uppercaseNames: string list = ["ALICE"; "BILLY"]

Naming convention related to functions

It's usual in F♯ to use short names:

  • x, y, z : parameters for values of the same type

  • f, g, h : parameters for input functions

  • xs : list of x

  • _ : discard an element not used (like in C♯ 7.0)

ā˜ļø Suited for a short function body or for a generic function:

// Function that simply returns its input parameter, whatever its type
let id x = x

// Composition of 2 functions
let compose f g = fun x -> g (f x)

Piping

Pipe operator |> : same idea that in UNIX with | → value |> function send a value to a function → match left-to-right reading order: "subject verb" → same order than with OOP: object.method

let a = 2 |> add 3  // to read "2 + 3"

// We pipe a list to the "List.filter predicate" function
let evens = [1;2;3;4;5] |> List.filter (fun x -> x % 2 = 0)
// ā‰ƒ C♯
var a = 2.Add(3);
var nums = new[] { 1, 2, 3, 4, 5 };
var evens = nums.Where(x => x % 2 == 0);

Pipeline: chain of pipings

Style of coding to emphasize the data flowing from functions to functions → without intermediary variable šŸ‘

Similar to a built-in fluent API → no need to return the object at the end of each method šŸ‘

// Short syntax: in a single line fitting the screen width
let res = [1;2;3;4;5] |> List.filter (fun x -> x % 2 = 0) |> List.sum

// More readable with line breaks
let res' =
    [1; 2; 3; 4; 5]
    |> List.filter isOdd  // With `let isOdd x = x % 2 <> 0`
    |> List.map square    //      `let square x = x * x`
    |> List.map addOne    //      `let addOne x = x + 1`

If/then/else expression

In F♯, if/then(/else) is an expression, not a statement, so every branch (then and else) should return a value and both returned values should be type-compatible.

let isEven n =
    if n % 2 = 0 then
        "Even"
    else
        "Odd"

šŸ’” if b then x else y ā‰ƒ C♯ ternary operator b ? x : y

ā˜ When then returns "nothing", else is optional:

let printIfEven n msg =
    if n |> isEven then
        printfn msg

šŸ’” We can use elif keyword instead of else if.

Match expression

let translateInFrench civility =
    match civility with
    | "Mister" -> "Monsieur"
    | "Madam"  -> "Madame"
    | "Miss"   -> "Mademoiselle"
    | _        -> ""   // šŸ‘ˆ wilcard `_`

Equivalent in C♯ 8 :

public static string TranslateInFrench(string civility) =>
    civility switch {
        "Mister" => "Monsieur"
        "Madam"  => "Madame"
        "Miss"   => "Mademoiselle"
        _        => ""
    }
PreviousBasesNextRules

Last updated 21 days ago

Was this helpful?

šŸ”— by Mark Seemann

When x, y, and z are great variable names