Header menu logo structured_programming_in_fsharp

Fixed points

Let's take a look at a common implementation of a function for calculating Fibonacci numbers:

let fibonacci0 n =
  let mutable i, a, b = 0, 0, 1

  while n <> i do
    let next = a + b
    a <- b
    b <- next
    i <- i+1
    
  a

fibonacci0 10

55

Most people think since functional programing discourages mutation, that means tying their hands and being useless. However, there's a communication failure here. Functional programming is valuable, not because discourages mutation, but because it encourages structured programming and composability.

The above function contains one of those patterns that appear everywhere, and functional programming can help you to recognize and reuse it more than imperative programming. Let's take a look at the power function and how it allows to implement Fibonacci:

let rec power (f: 'a -> 'a) (initial: 'a, n: int) =
  if n = 0 then initial else power f (f initial, n - 1)

let fibonacci1 n = power (fun (a, b) -> (b, a + b)) ((0, 1), n) |> fst

Now you can see how fibonacci1 is verbose in those aspects essential to Fibonacci: the initial state and how it transitions to the next. The rest is left to the power function.

Another thing while loops help us to find are fixed points, i.e. for a function f a value c that holds f c = c. But this pattern can be disentangled from while loops as well:

let fixedPoint (f: 'a -> 'a) (initial: 'a) =
  let rec loop (prev: 'a) (current: 'a) =
    if prev = current then prev else loop current (f current)

  loop initial (f initial)

Euclid's Algorithm for calculating the Greatest Common Divisor between two positive integers relies on finding a fixed point:

let gcd0 (a:int, b:int) =
  let mutable x = a
  let mutable y = b
  while x <> y do
    if x > y then x <- x - y else y <- y - x
  
  x

gcd0 (12,8)

4

Now, relying on our fixedPoint function we get:

let step (x: int, y: int) =
  if x > y then x - y, y
  else if x < y then x, y - x
  else x, y

let gcd1 = fixedPoint step >> fst

gcd1 (12, 8)

4

This time we discover there's a fixed point when dividing successive elements of the Fibonacci sequence. That number is known as Golden Ratio.

let fibonacci = Seq.unfold (fun (a, b) -> Some(a, (b, a + b))) (0, 1)

let fixedPointSeq (xs: 'a seq) = xs |> Seq.pairwise |> Seq.find (fun (a,b) -> a = b) |> fst

fibonacci |> Seq.pairwise |> Seq.map (fun (a,b) -> double b / double a ) |> fixedPointSeq

1.618033988749895

Functions power and fixedPoint are inspired by the power operator in APL (⍣)

val fibonacci0: n: int -> int
val n: int
val mutable i: int
val mutable a: int
val mutable b: int
val next: int
val power: f: ('a -> 'a) -> initial: 'a * n: int -> 'a
val f: ('a -> 'a)
'a
val initial: 'a
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

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

--------------------
type int<'Measure> = int
val fibonacci1: n: int -> int
val a: int
val b: int
val fst: tuple: ('T1 * 'T2) -> 'T1
val fixedPoint: f: ('a -> 'a) -> initial: 'a -> 'a (requires equality)
val f: ('a -> 'a) (requires equality)
val initial: 'a (requires equality)
val loop: prev: 'a -> current: 'a -> 'a (requires equality)
val prev: 'a (requires equality)
val current: 'a (requires equality)
val gcd0: a: int * b: int -> int
val mutable x: int
val mutable y: int
val step: x: int * y: int -> int * int
val x: int
val y: int
val gcd1: (int * int -> int)
val fibonacci: int seq
module Seq from Microsoft.FSharp.Collections
val unfold: generator: ('State -> ('T * 'State) option) -> state: 'State -> 'T seq
union case Option.Some: Value: 'T -> Option<'T>
val fixedPointSeq: xs: 'a seq -> 'a (requires equality)
val xs: 'a seq (requires equality)
Multiple items
val seq: sequence: 'T seq -> 'T seq

--------------------
type 'T seq = System.Collections.Generic.IEnumerable<'T>
val pairwise: source: 'T seq -> ('T * 'T) seq
val find: predicate: ('T -> bool) -> source: 'T seq -> 'T
val a: 'a (requires equality)
val b: 'a (requires equality)
val map: mapping: ('T -> 'U) -> source: 'T seq -> 'U seq
Multiple items
val double: value: 'T -> double (requires member op_Explicit)

--------------------
type double = System.Double

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

Type something to start searching.