11.3.4 delay values in F #
The delay values (lazy value) in F # are represented by a lazy calculation, that is, only if the value is needed. In the previous section, we implemented a similar function with C # functions, and the delay values were automatically computed only once and the results were remembered.
The best way to explore this functionality is in F # Interactive. Listing 11.16 is illustrated with a script.
[
The error with the manifest number appears again.
]
Listing 11.16 describes the delay value (F # Interactive)
> Let foo (n) =
PRINTFN "foo (%d)" N
n <= 10;;
Val foo:int¨c> bool
> Let n = Lazy foo (10);; | [1]
Val n:lazy<bool> = Value is notcreated. |
> N.value;; <--call Foo to get results
Foo (10) | [2]
Val It:bool = True |
> N.value;; <--Immediate return results
Val It:bool = True [3]
We first write a function similar to the C # Foo method, which writes to the console to track the calculated time, and the second command uses the Lazy keyword of F # [1]. If the expression is tagged with lazy, then the expression is not evaluated immediately and is packaged as a delay value. As you can see from the output, the Foo function has not been called, and the value created is the lazy<bool> type, which indicates that the delay value of the Boolean value can be computed.
On the next line, you can see that the delay value has a member, value [2], which performs a deferred calculation, and here, the Foo function is called. The last command accesses Value [3] again, and does not recalculate. If you look at the output, you will see that the Foo function is not called. This means that we can clearly see that,lazy< ' t> is a mutable type. If we use purely functional functions with no side-effects, as their parameters, we cannot observe this phenomenon.
In the first few chapters, we have emphasized the use of this functional approach to viewing data types, and have found it useful to know what kind of action is required when working with types. Now, let's look at the delay value in this view.
Using actions to specify delay values
If the F # language does not have a lazy keyword and does not allow objects with attributes, you might need two operations, one for building the delay value, and the other for extracting the actual value:
Val Create: (Unit-T)->lazy< ' t>
Val force:lazy< ' t>¨c> ' T
As you can see, the parameters of the create operation are a function value, wrapped in lazy< ' t> values. In functional programming, there are other types of deferred computations, so when a function's parameter is a function of type ' T, the return type is generic, and the type may be expressed as a deferred calculation. The signature of the force operation is even simpler, simply by extracting the actual value from the type in which it is packaged. This signature does not tell us how the actual value is packaged, but because of the force operation, we can always extract it.
In the next chapter we will see that this kind of abstract description of types is useful in functional programming, in terms of the ability to handle basic operations. While F # provides a way to handle deferred values, it is more convenient than using functions, but it is helpful to understand basic operations (primitive operations).
We began to discuss the motive of delay value, is not to implement the logic or operator, calculate the right-hand side of the parameter value, depends on whether the need, when needed. Now, we'll try to arm ourselves with new knowledge of the delayed values.
11.3.4 delay values in F #