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
  • Currying
  • Definition
  • Partial application
  • Syntax of F♯ functions
  • IntelliSense with Ionide
  • .NET compilation of a curried function
  • Unified function design
  • Parameters order

Was this helpful?

Edit on GitHub
  1. Functions

Concepts

Currying

Definition

Consists in transforming :

  • a function taking N parameters Func<T1, T2, ...Tn, TReturn> in C♯

  • into a chain of N functions taking 1 parameter Func<T1, Func<Tn, ...Func<Tn, TReturn>>

Partial application

Calling a function with fewer arguments than its number of parameters.

  • Possible thanks to currying

  • Returns a function taking the remaining number of arguments as parameters.

// Template function with 2 parameters
let insideTag (tagName: string) (content: string) =
    $"<{tagName}>{content}</{tagName}>"

// Helpers with a single parameter `content`
// `tagName` is fixed by partial application
let emphasize = insideTag "em"     // `tagName` fixed to "em"
let strong    = insideTag "strong" // `tagName` fixed to "strong"

// Equivalent less elegant but more explicit
let em content = insideTag "em" content
val insideTag: tagName: string -> content: string -> string
val emphasize: (string -> string)  // 👈 (1)(2)
val em: content: string -> string
  1. Loss of parameter names (content: string becomes just string)

  2. Signature of a function value, hence the parentheses ((string -> string))

Syntax of F♯ functions

Parameters separated by spaces:

  • Indicates that functions are curried

  • Hence the -> in the signature between parameters

let fn () = result         // unit -> TResult
let fn arg = ()            // T    -> unit
let fn arg = result        // T    -> TResult

let fn x y = (x, y)        // T1 -> T2 -> (T1 * T2)

// Equivalent, explicitly curried:
let fn x = fun y -> (x, y) // 1. With a lambda
let fn x =                 // 2. With a named sub-function
    let fn' y = (x, y)     // 👈 `x` captured from the outer scope
    fn'

IntelliSense with Ionide

💡 In VsCode with Ionide, IntelliSense provides a more readable description of functions, putting each argument in a new line:

let pair x y = (x, y)
// val pair:
//    x: 'a ->
//    y: 'b
//    -> 'a * 'b

let triplet x y z = (x, y, z)
// val triplet:
//    x: 'a ->
//    y: 'b ->
//    z: 'c
//    -> 'a * 'b * 'c

.NET compilation of a curried function

☝ A curried function is compiled differently depending on how it's called!

  • Basically, it is compiled as a method with tuple-parameters → Viewed as a regular method when consumed in C♯

module A =
    let add x y = x + y
    let value = 2 |> add 1
public static class A
{
    public static int add(int x, int y) => x + y;
    public static int value => 3;
}
  • When partially applied, the function is compiled as a pseudo Delegate class extending FSharpFunc<int, int> with an Invoke method that encapsulates the 1st supplied arguments.

module A =
    let add x y = x + y
    let addOne = add 1
public static class A
{
    internal sealed class addOne@3 : FSharpFunc<int, int>
    {
        internal static readonly addOne@3 @_instance = new addOne@3();

        internal addOne@3() { }

        public override int Invoke(int y) => 1 + y;
    }

    public static FSharpFunc<int, int> addOne => addOne@3.@_instance;

    public static int add(int x, int y) => x + y;
}

Unified function design

unit type and currying make it possible to design functions simply as:

  • Takes a single parameter of any type

    • including unit for a “parameterless” function

    • including another (callback) function

  • Returns a single value of any type

    • including unit for a “return nothing” function

    • including another function

👉 Universal signature of a function in F♯: 'T -> 'U.

Parameters order

Between C♯ and F♯, the parameter concerning the main object (the this in case of a method) is not placed in the same place:

  • In a method extension C♯, the this object is the 1st parameter.

    • E.g. items.Select(x => x)

  • In F♯, the main object is rather the last parameter: (it's called the data-last style)

    • E.g. List.map (fun x -> x) items

The data-last style favors :

  • Pipeline: items |> List.map square |> List.sum

  • Partial application: let sortDesc = List.sortBy (fun i -> -i)

  • Composition of partially applied functions up to param “data”.

    • (List.map square) >> List.sum

⚠️ There can be some friction .NET/BCL methods because the BCL is also data-first driven. The solution is to wrap the method in a new curried function having parameters sorted in an order more F♯ piping friendly.

let startsWith (prefix: string) (value: string) =
    value.StartsWith(prefix)

💡 Tips

Prefer to put 1st the most static parameters = those likely to be predefined by partial applications.

E.g.: “dependencies” that would be injected into an object in C♯.

👉 Partial application is a way to implement the dependency injection in F♯.

PreviousSignatureNextSyntax

Last updated 2 months ago

Was this helpful?

Caution : partial application impacts the signature:

Example: F♯ then C♯ equivalent, based on a simplified version from :

Example: F♯ then C♯ equivalent, based on a simplified version from :

⚠️
SharpLab
SharpLab