Lazy initialization is a creational design pattern in which computing a value or instantiation of (an) object(s) is delayed until the first time it is needed. The purpose of this article is to demonstrate the implementation of a general Lazy Initialization pattern using the F# language. Although F# has built-in faculties for dealing with Lazy Initialization it will be instructive if you are coming from a C# background to see how this can be implemented in the F# language.

F# is not implicitly lazy, however it is worthwhile to note that the keyword “eager” is reserved for future use and I have no idea why. As it is, right now, F# is eagerly evaluated and we have to tell the compiler to “be lazy here”. With regard to explicit laziness we have a few approaches at our disposal. Let’s start out with the core concepts and then work toward a solid implementation of Lazy Initialization.

Goals:

  1. Delay the binding of values
  2. Cache the results such that the computation is performed only once

The correct implementation of Lazy Initialization consists of two primary goals; first, an expensive computation must be delayed until the value is actually needed to continue executing the program. Second, the memoization of the result is needed so that additional requests for the value do not perform the computation again.

  1: // Delay with unit.
  2: type ComplicatedObject = System.Object
  3: let o     = new ComplicatedObject() // val a  : ComplicatedObject
  4: let f  () = new ComplicatedObject() // val a' : unit -> ComplicatedObject

In this example we are type aliasing System.Object to a type named ComplexObject, which saves me the hassle of creating anything complex for purposes of demonstration. On line 3 a new instance of ComplicatedObject is created and immediately bound to the value o. In line 4 we have created a thunk, by defining a function, the type here happens to be:

\(unit \rightarrow ComplicatedObject\)

The thunk is bound to the value f. Line 4 is really all it takes to delay binding, which is the first requirement listed in the primary goals of Lazy Initialization. A function like f used to delay the creation of ComplicatedObjects. When we evaluate f with unit, we receive a new ComplicatedObject in return, which can then be bound to some other value.

Although we shouldn’t neglect the consequences of eager evaluation when considering what problems, other than the *cough* obvious performance cost of initializing ComplicatedObject entails. Consider then, the following situation involving Schrödinger’s Cat:

  5: type Cat =
  6:   | Alive
  7:   | Dead
  8: 
  9: let whatsInTheBox () =
 10:   let aliveOrDead =
 11:     match (new System.Random()).Next 2 with
 12:     | 0 -> Alive
 13:     | _ -> Dead
 14:   let alive = printfn "A happy kitty"
 15:   let dead  = printfn "A dead kitty."
 16:   match aliveOrDead with
 17:   | Alive -> alive
 18:   | Dead  -> dead
 19: 
 20: whatsInTheBox ()

Now, when we ask whatsInTheBox () we find that our side effects are in the not-so-super position of being eagerly bound to the values alive : unit and dead : unit.

A happy kitty.
A dead kitty.
val it : unit = ()

So we use the technique on line 4 to instead create thunks to printfn. Since we’re really going to be opening the box this time we’ll also return the Cat to the caller.

 21: let openTheBox () =
 22:   let aliveOrDead =
 23:     match (new System.Random()).Next 2 with
 24:     | 0 -> Alive
 25:     | _ -> Dead
 26:   let alive = fun () -> printfn "A happy kitty."
 27:   let dead  = fun () -> printfn "A dead kitty."
 28:   match aliveOrDead with
 29:   | Alive -> alive
 30:   | Dead  -> dead
 31: 
 32: openTheBox ()

This behaves as intended, delaying the call to printfn until it is absolutely necessary. I’ve used a different syntax for lines 26 and 27 from line 4. Declarations such as: let f () = () and let f = fun () -> () are structurally identical. Deciding which syntax to use and where is a li.

A happy kitty.
val it : SchrodingersCat = Alive

In practice, the technique we’re using here is a bit cumbersome. If you had many types for which you needed to delay construction, you would have to wrap each of their default constructors in functions with different names. Or even a type with many properties whos construction is delayed until they are first accessed could potentially cause us to write a lot of repetitive code. Since F# treats functions as values we can create some higher order generalizations of delay:

 33: // Generalizing delay.
 34: let delay    x ()       = x // val delay    : 'a -> unit -> 'a
 35: let delayf   f x ()     = f x
 36: let delayAny x (_ : 'b) = x // val delayAny : 'a ->  'b  -> 'a
 37: let delayNew<'a when 'a : (new : unit -> 'a)> () = new 'a()
 38: 
 39: delayNew<ComplicatedObject>     // val it : (unit -> ComplicatedObject)
 40: delayNew<ComplicatedObject> ()  // val it : ComplicatedObject

We’ve accomplished our first goal of understanding how to delay values in F#, but what about the second? In order to cache the result of computations we will need a type to represent the kinds of results a computation may result in. I’ll use the word Idle as a synonym for Lazy so as to not conflict with the built-in lazy syntax in F#. First of all the result of the computation can either be currently delayed, already cached, or some exceptional case. We can use a Discriminated Union to express that.

 41: // our own lazy type implementation
 42: type IdleState<'a> =
 43:   | Delayed   of (unit -> 'a)
 44:   | Cached    of 'a
 45:   | Exception of System.Exception

Now we will construct an Idle<'a> type to encapsulate and mutate IdleState<'a>. We will need a static member to create instances of Idle, a property that tells us if the delayed value has already been computed, and finally a way to force the computed value. We can use a mutable record type augmented with the appropriate members to pull this off. However, with mutability there creeps in the hidden danger of shared state. We’ll get around that easily in F# using the built-in lock function.

 47: type Idle<'a> = 
 48:   { mutable status : IdleState<'a> }
 49:   static member Create f = 
 50:     {status = (Delayed (f))}
 51:   member x.IsValueCreated 
 52:     with get() =
 53:       lock x.status (delay <|
 54:         match x.status with 
 55:         | Cached _  -> true 
 56:         | _         -> false)
 57:   member x.Force () = 
 58:     lock x.status (delay <|
 59:       match x.status with
 60:       | Cached    v -> v
 61:       | Delayed   f -> try 
 62:                          let v = f () in 
 63:                          x.status <- (Cached(v))
 64:                          v
 65:                        with e -> 
 66:                          x.status <- Exception(e)
 67:                          reraise()
 68:       | Exception e -> raise e)
 69:   member x.Value 
 70:     with get() = x.Force ()
 71: 
 72: module Idle =
 73:   let create  (f : unit -> 'a) = Idle.Create(f)
 74:   let force   (x : Idle<'a>)   = x.Value

Here is how we would go about using our Idle type. First I define the ackerman function, something that should eat up a few cycles so we can see the difference between creating the delayed computation and forcing it to be computed.

 75: // a vicious recursive function
 76: let rec ackerman = function
 77:   | (m,n) when m = 0I -> n + 1I
 78:   | (m,n) when n = 0I -> ackerman (m - 1I, 1I)
 79:   | (m,n)             -> ackerman (m - 1I, ackerman (m, n-1I))
 80: 
 81: // delayed by our Idle mechanism
 82: let ack3_9 = Idle.create(delayf ackerman (3I,9I))
 83: 
 84: ack3_9.Value

Fortunately, we don’t actually have to do all of this ourselves. F# provides some syntactical sugar for us in the form of a lazy keyword and a Lazy module that provides a functional wrapper over the type Lazy in the .NET BCL. Using these faculties is strait forward, and the lazy keyword allows us to create a Lazy with any valid F# expression. So here is a lazy explination of F# lazy…

 85: let y = Lazy.Create(fun () -> 5) // give it a thunk
 86: let x = lazy (printfn "automatically wraps your expression in a thunk",37)
 87: 
 88: y.IsValueCreated                    // No
 89: x.IsValueCreated                    // No
 90: y.Force()                           // This will force y'
 91: y.IsValueCreated                    // Yes
 92: x.IsValueCreated                    // No
 93: let result = y.Value + (snd x.Value)// Uses chached y', forces x'
 94: y.IsValueCreated                    // Yes
 95: x.IsValueCreated                    // Yes
 96: x.Value                             // It's cached
 97: result // is bound to the value of 42 due to the forcing of x' and y'
 98: 
 99: // this should verify that the Next System.Random has been computed only once
100: let multipleForcing = lazy ((new System.Random()).Next 100)
101: [for x in 1..10 do yield multipleForcing.Force()]
102: 
103: let add a b = a + b                  // Function not lazy
104: let theSum  = add 41 1               // Value not lazy
105: let sum a b = lazy ( a + b )         // Function is lazy
106: let theSum' = sum 41 1               // Value is lazy
107: theSum'.Force()                      // Force it when you want
108: 
109: module Lazy =                        
110:   let force (x : Lazy<'a>) =         // Make a helper function
111:     x.Force()
112: 
113: Lazy.force <| sum 5 37               // Use it like this

In closing, we’ve taken a look at how to build our own type implementing a Lazy Initialization pattern in the F# language that is made thread-safe by using the F# Operators.lock function wherever the state is accessed. We’ve also breifly looked into the F# Lazy module and lazy keyword concluding nothing beats the ability to use lazy as a keyword that implicitly thunks any expression!

F# Web Snippets
namespace System
type Object =
  class
    new : unit -> obj
    member Equals : obj -> bool
    member GetHashCode : unit -> int
    member GetType : unit -> System.Type
    member ToString : unit -> string
    static member Equals : obj * obj -> bool
    static member ReferenceEquals : obj * obj -> bool
  end

Full name: System.Object
val o : ComplicatedObject

Full name: Creational.DesignPatterns.LazyInitialization.o
type ComplicatedObject = System.Object

Full name: Creational.DesignPatterns.LazyInitialization.ComplicatedObject
val f : unit -> ComplicatedObject

Full name: Creational.DesignPatterns.LazyInitialization.f
type Cat =
  | Alive
  | Dead

Full name: Creational.DesignPatterns.LazyInitialization.Cat

  type: Cat
  implements: System.IEquatable<Cat>
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<Cat>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
union case Cat.Alive: Cat
union case Cat.Dead: Cat
val whatsInTheBox : unit -> unit

Full name: Creational.DesignPatterns.LazyInitialization.whatsInTheBox
val aliveOrDead : Cat

  type: Cat
  implements: System.IEquatable<Cat>
  implements: System.Collections.IStructuralEquatable
  implements: System.IComparable<Cat>
  implements: System.IComparable
  implements: System.Collections.IStructuralComparable
type Random =
  class
    new : unit -> System.Random
    new : int -> System.Random
    member Next : unit -> int
    member Next : int -> int
    member Next : int * int -> int
    member NextBytes : System.Byte [] -> unit
    member NextDouble : unit -> float
  end

Full name: System.Random
val alive : unit

  type: unit
  implements: System.IComparable
val printfn : Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val dead : unit

  type: unit
  implements: System.IComparable
val openTheBox : unit -> (unit -> unit)

Full name: Creational.DesignPatterns.LazyInitialization.openTheBox
val alive : (unit -> unit)
val dead : (unit -> unit)
val delay : 'a -> unit -> 'a

Full name: Creational.DesignPatterns.LazyInitialization.delay
val x : 'a
val delayf : ('a -> 'b) -> 'a -> unit -> 'b

Full name: Creational.DesignPatterns.LazyInitialization.delayf
val f : ('a -> 'b)
val delayAny : 'a -> 'b -> 'a

Full name: Creational.DesignPatterns.LazyInitialization.delayAny
val delayNew : unit -> 'a (requires default constructor)

Full name: Creational.DesignPatterns.LazyInitialization.delayNew
type unit = Unit

Full name: Microsoft.FSharp.Core.unit

  type: unit
  implements: System.IComparable
type IdleState<'a> =
  | Delayed of (unit -> 'a)
  | Cached of 'a
  | Exception of System.Exception

Full name: Creational.DesignPatterns.LazyInitialization.IdleState<_>
union case IdleState.Delayed: (unit -> 'a) -> IdleState<'a>
union case IdleState.Cached: 'a -> IdleState<'a>
union case IdleState.Exception: System.Exception -> IdleState<'a>
type Exception =
  class
    new : unit -> System.Exception
    new : string -> System.Exception
    new : string * System.Exception -> System.Exception
    member Data : System.Collections.IDictionary
    member GetBaseException : unit -> System.Exception
    member GetObjectData : System.Runtime.Serialization.SerializationInfo * System.Runtime.Serialization.StreamingContext -> unit
    member GetType : unit -> System.Type
    member HelpLink : string with get, set
    member InnerException : System.Exception
    member Message : string
    member Source : string with get, set
    member StackTrace : string
    member TargetSite : System.Reflection.MethodBase
    member ToString : unit -> string
  end

Full name: System.Exception

  type: System.Exception
  implements: System.Runtime.Serialization.ISerializable
  implements: System.Runtime.InteropServices._Exception
type Idle<'a> =
  {mutable status: IdleState<'a>;}
  with
    member Force : unit -> 'a
    member IsValueCreated : bool
    member Value : 'a
    static member Create : f:(unit -> 'a0) -> Idle<'a0>
  end

Full name: Creational.DesignPatterns.LazyInitialization.Idle<_>
Idle.status: IdleState<'a>
static member Idle.Create : f:(unit -> 'a0) -> Idle<'a0>

Full name: Creational.DesignPatterns.LazyInitialization.Idle`1.Create
val f : (unit -> 'a)
val x : Idle<'a>
property Idle.IsValueCreated: bool
val lock : 'Lock -> (unit -> 'T) -> 'T (requires reference type)

Full name: Microsoft.FSharp.Core.Operators.lock
member Idle.Force : unit -> 'a

Full name: Creational.DesignPatterns.LazyInitialization.Idle`1.Force
val v : 'a
Multiple items
val e : exn

  type: exn
  implements: System.Runtime.Serialization.ISerializable
  implements: System.Runtime.InteropServices._Exception


--------------------

val e : exn

  type: exn
  implements: System.Runtime.Serialization.ISerializable
  implements: System.Runtime.InteropServices._Exception
val e : exn

  type: exn
  implements: System.Runtime.Serialization.ISerializable
  implements: System.Runtime.InteropServices._Exception
val reraise : unit -> 'T

Full name: Microsoft.FSharp.Core.Operators.reraise
val e : System.Exception

  type: System.Exception
  implements: System.Runtime.Serialization.ISerializable
  implements: System.Runtime.InteropServices._Exception
val raise : System.Exception -> 'T

Full name: Microsoft.FSharp.Core.Operators.raise
property Idle.Value: 'a
member Idle.Force : unit -> 'a
val create : (unit -> 'a) -> Idle<'a>

Full name: Creational.DesignPatterns.LazyInitialization.Idle.create
static member Idle.Create : f:(unit -> 'a0) -> Idle<'a0>
val force : Idle<'a> -> 'a

Full name: Creational.DesignPatterns.LazyInitialization.Idle.force
val ackerman : System.Numerics.BigInteger * System.Numerics.BigInteger -> System.Numerics.BigInteger

Full name: Creational.DesignPatterns.LazyInitialization.ackerman
val m : System.Numerics.BigInteger

  type: System.Numerics.BigInteger
  implements: System.IFormattable
  implements: System.IComparable
  implements: System.IComparable<System.Numerics.BigInteger>
  implements: System.IEquatable<System.Numerics.BigInteger>
  inherits: System.ValueType
val n : System.Numerics.BigInteger

  type: System.Numerics.BigInteger
  implements: System.IFormattable
  implements: System.IComparable
  implements: System.IComparable<System.Numerics.BigInteger>
  implements: System.IEquatable<System.Numerics.BigInteger>
  inherits: System.ValueType
val ack3_9 : Idle<System.Numerics.BigInteger>

Full name: Creational.DesignPatterns.LazyInitialization.ack3_9
Multiple items
module Idle

from Creational.DesignPatterns.LazyInitialization

--------------------

type Idle<'a> =
  {mutable status: IdleState<'a>;}
  with
    member Force : unit -> 'a
    member IsValueCreated : bool
    member Value : 'a
    static member Create : f:(unit -> 'a0) -> Idle<'a0>
  end

Full name: Creational.DesignPatterns.LazyInitialization.Idle<_>
property Idle.Value: System.Numerics.BigInteger
val y : System.Lazy<int>

Full name: Creational.DesignPatterns.LazyInitialization.y
Multiple items
active recognizer Lazy: Lazy<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.( |Lazy| )

--------------------

type Lazy<'T> = System.Lazy<'T>

Full name: Microsoft.FSharp.Control.Lazy<_>
static member System.Lazy.Create : creator:(unit -> 'T) -> System.Lazy<'T>
val x : Lazy<unit * int>

Full name: Creational.DesignPatterns.LazyInitialization.x
property System.Lazy.IsValueCreated: bool
member System.Lazy.Force : unit -> 'T
val result : int

Full name: Creational.DesignPatterns.LazyInitialization.result

  type: int
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<int>
  implements: System.IEquatable<int>
  inherits: System.ValueType
property System.Lazy.Value: int
val snd : ('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
property System.Lazy.Value: unit * int
val multipleForcing : Lazy<int>

Full name: Creational.DesignPatterns.LazyInitialization.multipleForcing
val x : int

  type: int
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<int>
  implements: System.IEquatable<int>
  inherits: System.ValueType
val add : int -> int -> int

Full name: Creational.DesignPatterns.LazyInitialization.add
val a : int

  type: int
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<int>
  implements: System.IEquatable<int>
  inherits: System.ValueType
val b : int

  type: int
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<int>
  implements: System.IEquatable<int>
  inherits: System.ValueType
val theSum : int

Full name: Creational.DesignPatterns.LazyInitialization.theSum

  type: int
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<int>
  implements: System.IEquatable<int>
  inherits: System.ValueType
val sum : int -> int -> Lazy<int>

Full name: Creational.DesignPatterns.LazyInitialization.sum
val theSum' : Lazy<int>

Full name: Creational.DesignPatterns.LazyInitialization.theSum'
val force : Lazy<'a> -> 'a

Full name: Creational.DesignPatterns.LazyInitialization.Lazy.force
val x : Lazy<'a>