Simulate varargs in Dart

Simulate varargs in Dart and create a logs function with varargs

Dart doesn’t support varargs. So we can’t call a function or create a class that accepts variable numbers of arguments. And it is not handy in some cases.

Suppose we want to create a log function that will accept variable numbers of arguments. We can’t do that in Dart. We need to call the log function multiple times just to dump logs into the console for multiple items.

Again, we can’t also use variable numbers of named arguments in Dart.

For example, we can’t do this in Dart.

1
logs("Total item", items.length, key: key)

Dart initially supported varargs but removed them later for the sake of simplicity. There is also an issue in the dart-lang repo that has been open for a long time, since 2014!

So, it seems there’s no luck anytime soon for getting varargs back into the language!

But we can apply a neat trick to get this work in Dart for both positional and named variable numbers arguments!

We can override the noSuchMethod(Invocation invocation) method in an Object to simulate this. When a nonexistent method is called on an instance of an Object, it internally calls this method and we can get access to all positional and named arguments from this method using invocation.positionalArguments and invocation.namedArguments. Great!

Let’s code this. First, we need to define the function that accepts a variable number of positional and named arguments.

1
2
typedef OnCall = dynamic Function(
    List<dynamic> args, Map<Symbol, dynamic> kwargs);

Here for positional arguments, we use List<dynamic> args and for named arguments we use Map<Symbol, dynamic> kwargs .

Now let’s define the actual class that will be responsible for tricking Dart.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class VariadicFunction {
  VariadicFunction(this._onCall);

  final OnCall _onCall;

  @override
  noSuchMethod(Invocation invocation) {
    if (!invocation.isMethod) {
      super.noSuchMethod(invocation);
    }
    final arguments = invocation.positionalArguments;
    final kwArguments = invocation.namedArguments;
    return _onCall(arguments, kwArguments);
  }
}

Here, when we call an instance of VariadicFunction as a method, and as it is NOT a method, it will invoke the noSuchMethod , thus doing the trick!

Now it’s time to implement the logs() function that we have mentioned earlier using VariadicFunction.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
final logs = VariadicFunction((args, kwargs) {
  final positional = args.isNotEmpty ? args.join(' ') : '';
  final named = kwargs.isEmpty
      ? ''
      : kwargs
          .map((key, value) => MapEntry(
              [
                key.toString().replaceAll('Symbol("', "").replaceAll('")', ''),
                ': ',
                value
              ].join(''),
              ''))
          .keys
          .join(' ');

  dev.log(
    [positional, named].join(' '),
    time: DateTime.now(),
  );
}) as dynamic;

Here, we first joined all positional arguments as a string. For named arguments, we need to get the named arguments’ symbol names. But the toString() gives us Symbol(“name”) . So we are removing this object name by using replaceAll and then joining them as a string.

Then we print them on the console by using the log() function defined in the dart:developer module.

Last but not least, we need to erase the type of VariadicFunction by adding as dynamic . Otherwise, we can’t invoke it as a method as it is NOT a method and it will give us a compiler error.

Then when we invoke logs as a method, it will eventually invoke the noSuchMethod and we get a function with args and kwargs just as in Python!

Mission accomplished!

Now we can this function to logs and it will accept arbitrary numbers of positional and named arguments. Great!

Final Code: https://gist.github.com/SNNafi/ed9d4ee98ace0d5b8ec9184f6c468d39
Reference: https://stackoverflow.com/questions/13731631/creating-function-with-variable-number-of-arguments-or-parameters-in-dart

Built with Hugo
Theme Stack designed by Jimmy