Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do lazy evaluation in Dart?

Is there a native (language supported) lazy evaluation syntax? Something like lazy val in Scala.

I've gone through the docs, and could not find anything. There is only a chapter about "lazily loading a library", but it's not what I am asking.

Based on this research I incline to believe (please correct me if I'm wrong) that currently there is no such thing. But maybe you know of any plans or feature requests which will provide the functionality? Or maybe it was considered and rejected by the Dart team?

If indeed there is no native support for this, then what is the best practice (best syntax) for implementing lazy evaluation? An example would be appreciated.

Edit:

The benefits of the feature that I am looking for are mostly the same as in implementation in other languages: Scala's lazy val or C#'s Lazy<T> or Hack's __Memorize attribute:

  1. concise syntax
  2. delayed computation until the value is needed
  3. cache the result (the by-need laziness)
  4. don't break pure functional paradigm (explanation below)

A simple example:

class Fibonacci {

  final int n;
  int _res = null;

  int get result {
    if (null == _res) {
      _res = _compute(this.n);
    }
    return _res;
  }

  Fibonacci(this.n);

  int _compute(n) {
    // ...
  }
}

main(List<String> args) async {
  print(new Fibonacci(5).result);
  print(new Fibonacci(9).result);
}

The getter is very verbose and has a repetitive code. Moreover I can't make the constructor const because the caching variable _res has to be computed on demand. I imagine that if I had a Scala-like lazy feature then I would also have language support for having a constant constructor. That's thanks to the fact, that the lazy evaluated _res is referentially transparent, and would not be in the way.

class Fibonacci {

  final int n;
  int lazy result => _compute(this.n);

  const Fibonacci(this.n);  // notice the `const`

  int _compute(n) {
    // ...
  }
}

main(List<String> args) async {
  // now these makes more sense:
  print(const Fibonacci(5).result);
  print(const Fibonacci(9).result);
}
like image 206
Maciej Sz Avatar asked Oct 19 '15 16:10

Maciej Sz


People also ask

What is DART lazy?

Lazy initialization is now part of dart from the release 2.12. Simply add late modifier to the variable declaration. late MyClass obj = MyClass(); And this object will be initialized only when it is first used.

How do you check if a variable is initialized in Dart?

AVOID late variables if you need to check whether they are initialized. Dart offers no way to tell if a late variable has been initialized or assigned to. If you access it, it either immediately runs the initializer (if it has one) or throws an exception.

What is a lazy method?

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specifically to the instantiation of objects or other resources.


2 Answers

Update 2021

Lazy initialization is now part of dart from the release 2.12.

Simply add late modifier to the variable declaration

late MyClass obj = MyClass();

And this object will be initialized only when it is first used.

From the docs:

Dart 2.12 added the late modifier, which has two use cases:

  1. Declaring a non-nullable variable that’s initialized after its declaration.
  2. Lazily initializing a variable.

Checkout the example here: https://dartpad.dev/?id=50f143391193a2d0b8dc74a5b85e79e3&null_safety=true

class A {
  String text = "Hello";
  
  A() {
    print("Lazily initialized");
  }
  
  sayHello() {
    print(text);
  }
}

class Runner {
  late A a = A();
  run() async {
    await Future.delayed(Duration(seconds: 3));
    a.sayHello();
  }
}

Here the class A will be initialized only when it is used.

like image 146
rehman_00001 Avatar answered Sep 22 '22 08:09

rehman_00001


update2

From @lrn s comment - using an Expando for caching makes it work with const:

class Lazy<T> {
  static final _cache = new Expando();
  final Function _func;
  const Lazy(this._func);
  T call() {
    var result = _cache[this];
    if (identical(this, result)) return null;
    if (result != null) return result;
    result = _func();
    _cache[this] = (result == null) ? this : result;
    return result;
  }
}


defaultFunc() {
  print("Default Function Called");
  return 42;
}
main([args, function = const Lazy(defaultFunc)]) {
  print(function());
  print(function());
}

Try it in DartPad

update

A reusable Lazy<T> could look like below in Dart but that also doesn't work with const and can't be used in field initializers if the calculation needs to refer instance members (this.xxx).

void main() {
  var sc = new SomeClass();
  print('new');
  print(sc.v);
}

class SomeClass {
  var _v  = new Lazy<int>(() {
    print('x');
    return 10;
  });
  int get v => _v();
}

class Lazy<T> {
  final Function _func;
  bool _isEvaluated = false;
  Lazy(this._func);
  T _value;
  T call() {
    if(!_isEvaluated) {
      if(_func != null) {
        _value = _func();
      }
      _isEvaluated = true;
    }
    return _value;
  }
}

Try it in DartPad

original

Dart version of http://matt.might.net/articles/implementing-laziness/ using a closure to lazy evaluate:

void main() {
  var x = () { 
    print ("foo"); 
    return 10; 
  }();
  print("bar");
  print(x);
  // will print foo, then bar then 10.
  print('===');
  // But, the following Scala program:
  x = () { 
    print("foo"); 
    return 10; 
  };
  print ("bar");
  print (x());
  // will print bar, then foo, then 10, since it delays the computation of x until it’s actually needed.
}

Try it in DartPad

like image 27
Günter Zöchbauer Avatar answered Sep 26 '22 08:09

Günter Zöchbauer