features: delayed evaluation
I’d like to use the term delayed evaluation to cover following two features in scala: lazy var/val and byname parameter. They are not quite related to each other, but both are to postpone the evaluation of a given expression or block for the final result.
FT-1: lazy var/val
Defining a field as lazy is a useful approach when the field might not be accessed in the normal processing of your algorithms, or if running the algorithm will take a long time, and you want to defer that to a later time.
At present, I think its useful in following scenarios:
SC-1-1: field-initialization takes great efforts
It makes sense to use lazy on a class field if its initialization requires a long time to run, and we don’t want to do the job when we instantiate the class until we actually use the field.
|
|
In above example, the initialization of text needs to retrieve the contents of the text file /etc/passwd. But when this code is compiled and run, there is no output, because the text field isn’t initialized until it’s accessed. That’s how a lazy field works.
SC-1-2: field-initialization has dependencies
Sometimes we need to initialize fields in a specific order because they have dependency on the other. Then we may produce following ugly codes:
|
|
In this spark-streaming demo, the initialization of ssc depends on that of sc, which further depends on conf. We operate the initialize manually, thus we define these fields with var, and implement the lazy initialization in getters. The shortcoming is obvious, we have to restrict the access of these field through getters, otherwise we may get the null-valued ones! Moreover, defining var to these fields is not best-practice since they are readonly after initialization. A modified version via lazy val/var is as follows:
|
|
What if a lazy field depends on a non-lazy var, which is not properly initialzed? Can the instance be re-used after some NullPointerException-like error raised? This seems no problem as scala provides a tricky, as @ViktorKlang posted on Twitter:
Little known Scala fact: if the initialization of a lazy val throws an exception, it will attempt to reinitialize the val at next access.
You can check the details here: http://scalapuzzlers.com/#pzzlr-012
FT-2: by-name parameter
The by-name parameter can be considered equivalent to () => Int, which is a Function type that takes a Unit type argument. Besides from normal functions, it can also be used with an Object and apply to make interesting block-like calls.
SC-2-1: wrapper function
|
|
SC-2-2: Add syntactic sugar
|
|
Accompany with curry, we re-implement a while-loop in above example.