Lazy evaluation

This concept is very simple. By default, F# follows the eager evaluation (https://en.wikipedia.org/wiki/Eager_evaluation) strategy, or an expression is evaluated as soon as it is bound. The alternative strategy available in other functional programming languages is to postpone the calculations until their result is absolutely necessary. F# can be explicitly told where to use lazy evaluation; by default, it uses lazy evaluations only for sequences. Expressing lazy evaluation if F# is not complicated syntactically, the following binding serves the purpose as shown:

let name = lazy ( expression ) 

Here, name is bound to the result of calculating expression, but the calculation itself is postponed. The type of value name is a special one, that is, Lazy<'T>; it represents a wrapper over 'T, which is the type of the expression per se. The computation gets performed by calling the Force method of type Lazy<'T>, like this name.Force(). This action also unwraps the underlying type of Lazy, so the type of the name.Force() expression is 'T.

Take into account that this feature is not specific to F#; the Lazy<T> class is a part of the .NET framework class library of the System namespace.

It is important to understand that the expression is calculated only once, so if the expression wrapped into the lazy method has a side effect, it is performed only once on the expression calculation. Even if the calculation is forced another time, nothing will happen on the side; only the cached result will be returned.

Let's demonstrate this with the following snippet (Ch7_5.fsx):

let twoByTwo  = lazy (let r = 2*2 in 
  printfn "Everybody knows that 2*2=%d" r; r)  
twoByTwo.Force() 
twoByTwo.Force() 

The following screenshot shows how this code behaves in FSI:

Lazy evaluation

Lazy evaluation and side-effects

Note that the binding for twoByTwo did not bring any calculations to life, but it wrapped the future calculation into the Lazy type. Then, the first twoByTwo.Force() function performed the wrapped calculation, so the side-effect popped up. Finally, any consequent twoByTwo.Force() function will just repeatedly bring the result of the very first calculation without any side-effects.

The lazy evaluation pattern has its own niche in enterprise F# development. I often use it when in need of a resource that's probably being initialized; if this need really materializes, I want it to happen only once. For example, we can consider reading the Production environment configuration settings from Azure KeyVault when a service runs in the Production environment while using some other configuration information carrier in other environments, for example, environment variables pointing to data stubs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset