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
  • Memoization
  • Lazy expression
  • Lazy active pattern
  • Organizing functions
  • Methods
  • Method Examples
  • Function vs Method
  • Function vs Delegate
  • Interop with the BCL
  • void method
  • Calling a BCL method with N arguments
  • out Parameter - In C♯
  • out Parameter - In F♯
  • Instantiate a class with new?
  • Calling an overloaded method

Was this helpful?

Edit on GitHub
  1. Functions

Addendum

PreviousOperatorsNextQuiz

Last updated 21 days ago

Was this helpful?

Memoization

šŸ’” Idea: reduce the calculation time of a function

ā“ How to: cache results → next call with same arguments, return cached result

šŸ‘‰ In practice : function memoizeN from library

āš ļø Caution : As with any optimization, use it when you need it and check (measure) that it works without any additional inconvenience.

ā˜ Not to be confused with lazy expression...

Lazy expression

Syntax sugar to create a .NET Lazy<'T> object from an expression

  • Expression not evaluated immediately but on 1st request ()

  • Interesting for improving performance without overcomplexifying the code

let printAndForward x = printfn $"{x}"; x

let a = lazy (printAndForward "a")

let b = printAndForward "b"
// > b

printfn $"{a.Value} and {b}"
// > a
// > a and b

printfn $"{a.Value} and c"
// > a and c

Lazy active pattern

Extracts the value from a Lazy object

let triple (Lazy i) = 3 * i // Lazy<int> -> int

let v =
    lazy
        (printf "eval!"
         5 + 5) // Lazy<int>

triple v
// eval!val it: int = 30

triple v
// val it: int = 30

Organizing functions

3 ways to organize functions = 3 places to declare them:

  • Module: function declared within a module šŸ“

  • Nested : function declared inside a value or inside another function

    • šŸ’” Encapsulate helpers used just locally

    • ā˜ Parent function parameters accessible to function nested

  • Method : function defined as a method in a type...

Methods

  • Defined with the member keyword rather than let

  • Choice of the self-identifier: this, me, self, _...

  • Choice of the parameters style:

    • Tuplified: OOP style

    • Curried: FP style

Method Examples

type Product =
    { SKU: string; Price: float }

    // Tuple style, `this`
    member this.TupleTotal(qty, discount) =
        (this.Price * float qty) - discount

    // Curried style, `me`
    member me.CurriedTotal qty discount =
        (me.Price * float qty) - discount

Function vs Method

Feature
Function
Method

Naming convention

camelCase

PascalCase

Currying

āœ… yes

āœ… if not tuplified nor overridden

Named parameters

āŒ no

āœ… if tuplified

Optional parameters

āŒ no

āœ… if tuplified

Overload

āŒ no

āœ… if tuplified

Parameter inference (declaration)

āž– Possible

āž– yes for this, possible for the other parameters

Argument inference (usage)

āœ… yes

āŒ no, object type annotation needed

High-order function argument

āœ… yes

āž– yes with shorthand member, no with lambda otherwise

inline supported

āœ… yes

āœ… yes

Recursive

āœ… yes with rec

āœ… yes

Function vs Delegate

We can define a delegate in F# with the syntax: type Fn = delegate of args: Args -> Return

šŸ‘‰ A delegate adds a thin layer on top of a function signature, like an object with a single method Invoke:

  • Pros: we can use named arguments when invoking the delegate

  • Cons:

    • extra work to wrap the function (or the method) in the delegate and to execute the function by calling the Invoke(...) method

    • cumbersome syntax when there are several parameters, to differentiate between tuple and curried parameters:

      • Tuple: type MyFn = delegate of (int * int) -> int

      • Curried: type MyFn = delegate of int * int -> int

It's not a very common use case in F#.

Interop with the

void method

A .NET void method is seen in F♯ as returning unit.

let list = System.Collections.Generic.List<int>()
list.Add
(* IntelliSense Ionide:
abstract member Add:
   item: int
      -> unit
*)

Conversely, an F♯ function returning unit is compiled into a void method.

let ignore _ = ()
public static void ignore<T>(T _arg) {}

Calling a BCL method with N arguments

A .NET method with several arguments is "pseudo-tuplified":

  • All arguments must be specified (1)

  • Partial application of parameters is not supported (2)

  • Calls don't work with a real F♯ tuple āš ļø (3)

System.String.Compare("a", "b") // āœ… (1)
System.String.Compare "a","b"   // āŒ
System.String.Compare "a"       // āŒ (2)

let tuple = ("a","b")
System.String.Compare tuple     // āŒ (3)

out Parameter - In C♯

out used to have multiple output values from a method → Ex : Int32.TryParse, Dictionary<,>.TryGetValue :

if (int.TryParse(maybeInt, out var value))
    Console.WriteLine($"It's the number {value}.");
else
    Console.WriteLine($"{maybeInt} is not a number.");

out Parameter - In F♯

Output can be consumed as a tuple šŸ‘

  match System.Int32.TryParse maybeInt with
  | true, i  -> printf $"It's the number {value}."
  | false, _ -> printf $"{maybeInt} is not a number."

Instantiate a class with new?

// (1) new allowed but not recommended
type MyClass(i) = class end

let c1 = MyClass(12)      // šŸ‘
let c2 = new MyClass(234) // šŸ‘Œ mais pas idiomatique

// (2) IDisposable => `new` required, `use` replaces `let` (otherwise it's a compiler warning)
open System.IO
let fn () =
    use f = new FileStream("hello.txt", FileMode.Open)
    f.Close()

Calling an overloaded method

  • Compiler may not understand which overload is being called

  • Tips: call with named argument

let createReader fileName =
    new System.IO.StreamReader(path = fileName)
    // ā˜ļø Param `path` → `filename` inferred as `string`

let createReaderByStream stream =
    new System.IO.StreamReader(stream = stream)
    // ā˜ļø Param `stream` of type `System.IO.Stream`

Equivalent C# based on :

FSharpPlus
Thunk
SharpLab