50

I am looking for a way to create a function with a variable number of arguments or parameters in Dart. I know I could create an array parameter instead, but I would prefer to not do that because I'm working on a library where syntactic brevity is important.

For example, in plain JavaScript, we could do something like this (borrowed from here):

function superHeroes() {
  for (var i = 0; i < arguments.length; i++) {
    console.log("There's no stopping " + arguments[i]);
  }
}

superHeroes('UberMan', 'Exceptional Woman', 'The Hunk');

However, in dart, that code will not run. Is there a way to do the same thing in dart? If not, is this something that is on the roadmap?

Alexandre Ardhuin
  • 71,959
  • 15
  • 151
  • 132
plowman
  • 13,335
  • 8
  • 53
  • 53
  • https://github.com/dart-lang/language/issues/1014 (I would not hold my breath, it's open for 7.5 years) – tevemadar Jul 31 '21 at 10:33

6 Answers6

48

You can't do that for now.

I don't really know if varargs will come back - they were there some times ago but have been removed.

However it is possible to emulate varargs with Emulating functions. See the below code snippet.

typedef OnCall = dynamic Function(List arguments);

class VarargsFunction {
  VarargsFunction(this._onCall);
  
  final OnCall _onCall;

  noSuchMethod(Invocation invocation) {
    if (!invocation.isMethod || invocation.namedArguments.isNotEmpty)
      super.noSuchMethod(invocation);
    final arguments = invocation.positionalArguments;
    return _onCall(arguments);
  }
}

main() {
  final superHeroes = VarargsFunction((arguments) {
    for (final superHero in arguments) {
      print("There's no stopping ${superHero}");
    }
  }) as dynamic;
  superHeroes('UberMan', 'Exceptional Woman', 'The Hunk');
}
Alexandre Ardhuin
  • 71,959
  • 15
  • 151
  • 132
  • 1
    Wow thats a really neat solution. I do like the way dart handles arguments, but this would help with developers who like it the other way. – adam-singer Dec 05 '12 at 23:13
  • Very nice! I had not looked much into the InvocationMirror part of the language, so thanks for a great example. I tweaked your answer slightly to create a version that works on the current version of Dart. – plowman Dec 05 '12 at 23:18
  • I copy and paste above code, However I got `Too many positional arguments: 0 expected, but 3 found. Try removing the extra arguments.dart(extra_positional_arguments)` error during the `superHeroes('UberMan', 'Exceptional Woman', 'The Hunk');` line. I have to change `final superHeroes = VarargsFunction((arguments)` to `final dynamic superHeroes = VarargsFunction((arguments)` for fixing. – Donghua Liu Sep 16 '20 at 05:19
  • 1
    Dart is not a Javascript! Please don't use syntax like "final x = MyFunction( () {...});, it's not an idiomatic Dart, and don't use `dynamic` unless you really know what you are doing. – Sergey Molchanovsky Jul 13 '21 at 22:23
  • 1
    @SergeyMolchanovsky Why wouldn't it be idiomatic? `VarargsFunction` is a class that takes a function as a constructor parameter, and the function in this case is an anonymous function. It's just as idiomatic as calling `list.map((x) {...});`. And while this solution would certainly be classified as a hack, it requires the object to be typed as `dynamic` in order to work so this is a valid use of it. – Abion47 Nov 25 '21 at 16:04
11

Dart does indirectly support var-args as long as you aren't too much into syntactic brevity.

void testFunction([List<dynamic> args=[]])
{
  for(dynamic arg:args)
  {
    // Handle each arg...
  }
}

testFunction([0, 1, 2, 3, 4, 5, 6]);
testFunction();
testFunction([0, 1, 2]);

Note: You can do the same thing with named parameters, but you'll have to handle things internally, just in case if the user (of that function; which could be you) decides to not pass any value to that named parameter.


I would like to thank @Ladicek for indirectly letting me know that a word like brevity exists in English.

luizv
  • 618
  • 1
  • 12
  • 22
Mohit Shetty
  • 1,551
  • 8
  • 26
  • 2
    This is what I personally come up with before I open this page. After seeing all the answers here, I choose to keep using this solution because the other solution looks like a hack. I don't like how each function is defined. And yes, I'm not too much into syntactic brevity. – Chen Li Yong Nov 27 '20 at 15:25
8

This version:

  1. Works with both positional and keyword arguments.
  2. Supports typing of the return value.
  3. Works with modern Dart.
typedef VarArgsCallback = void Function(List<dynamic> args, Map<String, dynamic> kwargs);

class VarArgsFunction {
  final VarArgsCallback callback;
  static var _offset = 'Symbol("'.length;

  VarArgsFunction(this.callback);

  void call() => callback([], {});

  @override
  dynamic noSuchMethod(Invocation inv) {
    return callback(
      inv.positionalArguments,
      inv.namedArguments.map(
        (_k, v) {
          var k = _k.toString();
          return MapEntry(k.substring(_offset, k.length - 2), v);
        },
      ),
    );
  }
}

void main() {
    dynamic myFunc = VarArgsFunction((args, kwargs) {
      print('Got args: $args, kwargs: $kwargs');
    });
    myFunc(1, 2, x: true, y: false); // Got args: [1, 2], kwargs: {x: true, y: false}
}

Thanks, Alexandre for your answer!

Benjamin Lee
  • 1,014
  • 10
  • 24
Dev Aggarwal
  • 7,627
  • 3
  • 38
  • 50
7

I played around a little with Alexandre Ardhuin's answer and found that we can tweak a couple of things to make this work in the current version of Dart:

class VarArgsClass {
  noSuchMethod(InvocationMirror invocation) {
    if (invocation.memberName == 'superheroes') {
      this.superheroes(invocation.positionalArguments);
    }
  }

  void superheroes(List<String> heroNames) {
    for (final superHero in heroNames) {
      print("There's no stopping ${superHero}!");
    }
  }
}

main() {
  new VarArgsClass().superheroes('UberMan', 'Exceptional Woman', 'The Hunk');
}

This has lots of problems, including:

  • A warning is generated wherever you call superheroes() because the signature doesn't match your parameters.
  • More manual checking would need to be done to make sure the list of arguments passed to superheroes is really a List<String>.
  • Needing to check the member name in noSuchMethod() makes it more likely you'll forget to change the 'superheroes' string if you change the method name.
  • Reflection makes the code path harder to trace.

BUT if you are fine with all of those issues, then this gets the job done.

plowman
  • 13,335
  • 8
  • 53
  • 53
  • 1
    There is one other issue that I believe _has to_ be mentioned: it will only work if you pass more than one argument. If you pass only one string, you will call the original superheroes method and the noSuchMethod machinery will be ignored. – Ladicek Dec 06 '12 at 05:18
  • This method will no longer work in modern versions of Dart for a variety of reasons. – jamesdlin Mar 04 '23 at 19:54
5

If you are really into syntactic brevity, just declare a function/method with say 10 optional positional parameters and be done. It's unlikely someone will call that with more than 10 arguments.

If it sounds like a hack, that's because it is a hack. But I've seen the Dart team doing the same :-)

For example:

void someMethod(arg0, [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9]) {
  final args = [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9];

  args.removeWhere((value) => value == null);

  /* do something the the args List */
  print(args);
}
shennan
  • 10,798
  • 5
  • 44
  • 79
Ladicek
  • 5,970
  • 17
  • 20
2

For the example you've written, I think you're best off using a list. Sorry about that!

I'm looking at dartbug.com, but I don't see a feature request for this. You're definitely welcome to create one!

Shannon -jj Behrens
  • 4,910
  • 2
  • 19
  • 24