17

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);
}
Community
  • 1
  • 1
Maciej Sz
  • 11,151
  • 7
  • 40
  • 56
  • Can you please provide a concrete example of what you want to accomplish using lazy evaluation. There is no special language support and no discussion about it as far as I am aware. Many methods on List, Map, ... which are lazy evaluated. For example `map()`, `fold()`, `reduce()`, ... See for example http://stackoverflow.com/questions/20491777/dart-fold-vs-reduce. Maybe just closures do what you want. – Günter Zöchbauer Oct 19 '15 at 16:12
  • 2
    You can shorten your `int get result` to just `int get result => _res ??= _compute(this.n);` – smac89 Jul 27 '20 at 01:46

3 Answers3

17

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));
    print("First message");
    a.sayHello();
  }
}

Here class A will be initialized only after "First message" has been displayed.

intrepidis
  • 2,870
  • 1
  • 34
  • 36
rehman_00001
  • 1,299
  • 1
  • 15
  • 28
6

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

cubuspl42
  • 7,833
  • 4
  • 41
  • 65
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thank you for the answer. I am familiar with this article you linked. It's actually one of the reasons I am looking for a good *lazy evaluation* implementation in Dart. Both of the examples that you provided have flaws for what I am trying to achieve. First one is computed regardless whether the result is used or not. Second cannot be used with class members and constant constructors in Dart. Can you please take a look on the edit to my question? – Maciej Sz Oct 19 '15 at 19:28
  • I looked a bit more into it. See [DartPad](https://dartpad.dartlang.org/a4489204eb37f57ed067) for the code. I don't think there is a way to use lazy evaluation in combination with const in Dart. – Günter Zöchbauer Oct 20 '15 at 06:24
  • Static members are lazy initialized in Dart but I guess this is also only of limited use for your requirement. – Günter Zöchbauer Oct 20 '15 at 06:30
  • 3
    There is a way to get lazy cached evaluation and const-ness at the same time: Use a global `Expando` to hold the cached values instead of putting them on the object. See: https://dartpad.dartlang.org/8a668690499074a1b62f – lrn Oct 20 '15 at 06:48
  • Ready to use package: https://github.com/azilvl/lazy_evaluation – Yauheni Pakala May 08 '23 at 10:41
3

Update

int _val;
int get val => _val ??= 9;

Thanks @Nightscape

Old

I think this little snippet might help you...

int _val;
int get val => _val ?? _val = 9;
towhid
  • 2,778
  • 5
  • 18
  • 28