32

Recently I've been delving into Flutter's ecosystem and Dart has proven itself a neat and simple language.

Currently, I am looking for a best practice to run methods if an optional variable is not null.

In other words, I am looking for something in Dart that is like Kotlin's let operator :

variable?.let {
    doStuff();
    doABitMoreStuff();
    logStuff();
}

Anyone got any ideas or best practices around this?

I've looked into Dart's documentation and have found nothing that would fit my requirements.

King regards,

Ricardo Vieira
  • 1,738
  • 1
  • 18
  • 26
  • What are you trying to accomplish. I don't think there is a direct equivalent. I don't know Kotlin and the docs don't tell much. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html – Günter Zöchbauer Sep 03 '18 at 10:57
  • 2
    Probably `if(variable != null)` – EpicPandaForce Sep 03 '18 at 11:00
  • 1
    @EpicPandaForce I am trying to avoid the null check. That seems a bit java-like and given that dart is a recent language (like swift and kotlin) I was assuming it would have a similar 'let' key. – Ricardo Vieira Sep 03 '18 at 11:13
  • Dart has the `?.` operator too. Just no that sort of block yet – Rémi Rousselet Sep 03 '18 at 11:14
  • 1
    "I am trying to avoid the null check". But you are doing a null check, you just write it as `?` instead of `== null`. Note that `if (variable == null) { /* ... */ }` is much easier to read than `variable?.let { ... }` while it is essentially the same thing. – Vyacheslav Egorov Sep 03 '18 at 14:27

10 Answers10

27

With the new Dart extension functions, we can define:

extension ObjectExt<T> on T {
  R let<R>(R Function(T that) op) => op(this);
}

This will allow to write x.let(f) instead of f(x).

user3612643
  • 5,096
  • 7
  • 34
  • 55
  • 1
    This is slightly overcomplicated. The following works just fine: `R let(R Function(T) x) => x(this);` – Joshua Hyatt May 23 '22 at 19:14
  • @JoshuaHyatt in older Dart versions the “as” cast was required. – user3612643 May 23 '22 at 19:29
  • can some1 update for current version ? – Yogi Arif Widodo Jun 01 '22 at 08:56
  • A caveat to be aware of with this, that took me some hours to figure out, is that dynamics need to be cast for the let function to "exist": Won't work: `dynamic foo=3; foo.let((ouch){});`, Will work: `dynamic foo=3; (foo as int).let((ouch){});` – stobix Jul 05 '22 at 18:56
11

Dart's equivalent would be a null-aware cascade operator: The Dart approach would be a to use a null-aware cascade:

SomeType? variable = ...

variable
   ?..doStuff()
    ..doABitMoreStuff()
    ..logStuff();

The null-aware cascade works like the normal cascade, except that if the receiver value is null, it does nothing.


You could make your own using a static function though:

typedef T LetCallback<T>(T value);

T let<T>(T value, LetCallback<T> cb) {
  if (value != null) {
    return cb(value);
  }
}

Then used like that:

let<MyClass>(foo, (it) {

})
lrn
  • 64,680
  • 7
  • 105
  • 121
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • Thanks. Although this is the solution I am already implementing, it is not necessarily what I was looking for. I would live to have both a closure and the null checking inside the same construct, much like kotlin's let. – Ricardo Vieira Sep 03 '18 at 11:28
  • Dart doesn't have that. It probably won't until Dart has extensions methods. You could create your own using a static function. Although the syntax will be slightly different – Rémi Rousselet Sep 03 '18 at 11:37
  • @RicardoVieira I added a small example – Rémi Rousselet Sep 03 '18 at 11:43
5

We can do it with Dart 2.6 or later.

extension ScopeFunctionsForObject<T extends Object> on T {
  ReturnType let<ReturnType>(ReturnType operation_for(T self)) {
    return operation_for(this);
  }
}

usage: https://github.com/YusukeIwaki/dart-kotlin_flavor#let

YusukeIwaki
  • 59
  • 1
  • 2
  • It doesn't work on Flutter. It compiles but then in runtime: `exception: NoSuchMethodError: Class '_InternalLinkedHashMap' has no instance method 'let'. ` – Alexander Skvortsov Apr 20 '20 at 13:53
  • That's because your map variable has static type `dynamic`. Extension methods only work on non-`dynamic`-typed values. That's another reason to avoid `dynamic`. – lrn Mar 16 '21 at 22:24
2

The difference between x?.let{ } and if (x != null) { } in Kotlin is that the former promotes x to be non-nullable. Since Kotlin has non-nullable types, it prevents you from needing to write x!! to assert the non-nullability inside the block.

Dart doesn't have non-nullable types (yet), so that distinction isn't important in Dart. Just use if (x != null) { ... }. If Dart gets non-nullable types, you can be assured that the null check in the condition will also promote x to non-nullable inside the block (if x is a local variable, is not mutated inside the block, other restrictions may apply). (EDIT: Dart now has nullable types, and x != null promotes x to non-null.)

From your other comments, it sounds like you might be wanting the Kotlin behavior of x?.run { ... } instead, which binds this to x inside the lambda block. There is no corresponding feature in Dart. You cannot override this, it's always bound to the the object that the current method was called on, even inside nested closures which captures the value of this just as they capture other variables.

lrn
  • 64,680
  • 7
  • 105
  • 121
1

Using this extension:

extension Ext on Object? {
    void ifNotNull(Function() action) {
        if(this != null){
            action();
        }
    }
}

You can achieve something similar:

object.ifNotNull(() => {
    // safe code
});
Vapid
  • 701
  • 7
  • 27
0

Even though Dart doesn't have the let like behavior as of Kotlin but we can certainly emulate it with concise and readable code. Maybe something like this:

void main() {
  String str = null;

  str?.isEmpty ?? Function.apply(() {
    print("Hey there you cheeky null valued variable");
  }, []);
}
Shababb Karim
  • 3,614
  • 1
  • 22
  • 35
0

i implemented let function with extension function like this:

extension KotlinLet on Object?{
  void let(Function callback ){
    if (this != null) {
      callback();
    }
  }
 Object? also(){
    if (this != null) {
     return this;
    }
  }
}
kururu
  • 43
  • 5
0

You can simply use this package kotlin_flavor: https://pub.dev/packages/kotlin_flavor/install

sourav pandit
  • 8,647
  • 2
  • 19
  • 17
-1

I guess a closure does what you want

class Foo {
  var x = 42;
  void bar() {
    () {
      print(x);
      doStuff();
      doABitMoreStuff();
      logStuff();
    }();
  }
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    The closure is nice but I was looking for something like a nullcheck combined with a closure, pretty much like kotlin's let. – Ricardo Vieira Sep 03 '18 at 11:28
  • I got that when I saw the other comments but Remi was faster. It's always a good idea to state what problem or use case you want to solve instead mentioning features from other languages, frameworks, ... – Günter Zöchbauer Sep 03 '18 at 11:36
-2

There is no direct equivalent, because there is no need for it in Dart. Dart is a lot more like Java and you often end up with similar solutions.

There is almost no syntactic sugar in Dart. It's supposed to be easy to learn.

Also, Dart does not enforce strict null checks, all variables are nullable, and the language is single-threaded. That's why there is no need for let. Use if instead:

if(variable != null) {
  doStuff();
  doABitMoreStuff();
  logStuff();
}
boformer
  • 28,207
  • 10
  • 81
  • 66
  • 5
    What you propose does not answer the question. It has to be: ```dart if(variable != null) { variable..doStuff()..doABitMoreStuff()..logStuff(); } ``` I think `let` is more elegant in doing this than what we have in dart today. To implement it, we need what is called receivers (method pointers) in Dart. Having method pointers, will also allow Koltin style DSLs in Dart. – Ravi Teja Gudapati Sep 04 '18 at 10:42
  • 2
    To get that behavior in Kotlin, I believe (after cursory reading) that you should use `run` instead of `let`. The code here matches the `let` method of Kotlin in that it binds the `variable` object to the lambda parameter, not the default receiver. – lrn Sep 04 '18 at 10:59
  • @lrn exactly, `let` would give you access to the `variable` as `it`, perhaps OP is intending to use it as `this`? – Javier Mendonça Sep 04 '18 at 11:39