Lazy Evaluation of Arguments
Lazy evaluation can provide improved performance and allow code that would otherwise not terminate or cause errors. Lazy evaluation can be achieved using a nullary function argument.
Lazy evaluation in other languages
Haskell
Lazy evaluation is the default for all function arguments.
Java
Java has built-in lazy evaluation for binary boolean operators &&
and ||
. This permits code such as
if (a != null && a.toString().equals("a"))
{
System.out.prinltn("a is a");
}
if (b == null || !b.toString().equals("b"))
{
System.out.prinltn("b is not b");
}
This code would crash with a NullPointerException
if the
evaluation of the right hand side of the boolean operators would not be lazy.
Rust
Rust has a macro lazy!
to create a memoized lazily evaluated
value. Evaluation is explicit using *
, see the following code
taken from docs.rs:
fn expensive() -> i32 {
println!("I am expensive to evaluate!"); 7
}
fn main() {
let a = lazy!(expensive()); // Nothing is printed.
// Thunks are just smart pointers!
assert_eq!(*a, 7); // "I am expensive to evaluate!" is printed here
let b = [*a, *a]; // Nothing is printed.
assert_eq!(b, [7, 7]);
}
Apart from this, Rust seems to have no specific support for lazy evaluation of
arguments, functions with lazy arguments need to either use function types
or lazy_st::Thunk
.
F#
A lazily evaluatable expression in Rust is declared using lazy
(expr)
, evaluation is done via a call to Force
on the
expression. The type of a lazy expression created from an expression of
type T
is Lazy<T>
.
Here is an example taken from microsoft.com:
let x = 10
let result = lazy (x + 10)
printfn "%d" (result.Force())
Some developers are unhappy about F#'s lazy evaluation not being transparent and not memoized.
C#/.NET
A clazz Lazy<T>
provides lazy evaluation and result caching
in conjunction with specifc thread safety modes.
Syntax alternatives for Fuzion
Let's play around with syntax alternatives using a feature and
that implements boolean conjunction, a feature list
that creates
a list from a head and a lazy tail, and a lazy type_of
function:
Approach
Aspect
|
Explicit lambda | Implicit lambda with lazy keyword |
Implicit lambda | lazy feature |
---|---|---|---|---|
Idea |
Explicitly use function types, i.e., add |
A new keyword |
Use explicit function types at declaration and use, but automatically wrap incompatible actual arguments in a unary function on use. This is a special case of partial application with no arguments applied and no arguments remaining. |
Make |
Feature declaration |
|
|
|
|
Feature call |
|
|
|
|
Comment |
The developer is explicit at the feature declaration and at the call site. |
For a call to
|
The caller of
|
Maybe the most beautiful solution?
Need to decide to either use a value type around a function
or a
|
Pros&Cons |
🟡 explicit but cryptic at declaration |
✅ explicit at declaration |
🟡 explicit but cryptic at declaration |
✅ explicit at declaration |