language icon indicating copy to clipboard operation
language copied to clipboard

Feature Request: Variable Number of Arguments

Open DartBot opened this issue 10 years ago • 49 comments

This issue was originally filed by [email protected]


It would be nice to have variable numbers of arguments, a la python splats.

This would make porting python code to dart much cleaner.

For example, I would like to do something like this:

void myFunc(arg1, arg2, *anyRemainingArgs){
  var extraArg1 = anyRemainingArgs[0];
  var extraArg2 = anyRemainingArgs[1];
  ...
}

DartBot avatar Jan 23 '14 02:01 DartBot

Removed Type-Defect label. Added Type-Enhancement, Area-Language, Triaged labels.

sgjesse avatar Jan 23 '14 14:01 sgjesse

This comment was originally written by @seaneagan


Strawman:

varPositionals(required, [optional, String ...rest]) {   assert(rest is List<String>); } varNamed (required, {optional, String ...rest}) {   assert(rest is Map<Symbol, String>); }

(...rest is used by ES6)

DartBot avatar Jan 23 '14 15:01 DartBot

This comment was originally written by [email protected]


+1 for the Strawman in #­2. I like the '...' syntax, because it (1) semantically jibes for me, and (2) it echoes coffeescript varargs syntax, which folks are already familiar with.

DartBot avatar Jan 23 '14 15:01 DartBot

This comment was originally written by @zoechi


As it's just another sytax for a parameter of type List, why not just stay with List?

DartBot avatar Feb 01 '14 18:02 DartBot

This comment was originally written by [email protected]


I'm not sure that using a 'List' parameter has the effect I'm looking for.

If I used a 'List' parameter, wouldn't I have to explicitly put the arguments into a list when I call the function?

For example, if I have:

varPositionals (required, [optional, List theRest])

wouldn't I need to call this like:

varPositionals('required', 'optional', ['a', 'b', 'c'])

?

I'm looking instead for something more like python's behavior e.g. I want to call the function like this:

varPositionals('required', 'optional', 'a', 'b', 'c')

and have 'a', 'b', and 'c' get aggregated into the variable 'theRest'.

I hope that this helps to clarify.

DartBot avatar Feb 01 '14 19:02 DartBot

This comment was originally written by @zoechi


I understand your request but I'm not sure if special syntax is worth this little (IMHO) gain.

DartBot avatar Feb 01 '14 19:02 DartBot

This comment was originally written by [email protected]


This would make porting python code to dart much cleaner.

As a user of Dart, I personally do not find it important to make it easy to port code from Python to Dart, or from any language for that matter (perhaps except for JavaScript). After all, Dart and Python are different languages.

I am not saying that supporting variadic functions in Dart is useless. But “Python supports them, so Dart should also support them to make porting easy” is a weak argument.

By the way, it is possible to emulate variadic functions in Dart by a dirty hack in case you need them for some reason. See my post at http://yuasakusa.github.io/dart/2014/01/23/dart-variadic.html.

DartBot avatar Feb 01 '14 22:02 DartBot

This comment was originally written by [email protected]


Yah, I agree that supporting varargs just for the sake of porting python code is not a good argument.

However, I would say that supporting varargs for the sake of having a clean, flexible, readable langauge is a good argument.

We can get around the lack of vararg support using tricks like the one described in Yu Asakusa's blog post. (Nice, by the way!). But to me built-in vararg support would still be nice to have.

At this point I suppose the decision on this rests in the hands of the Dart committee (or however these things get decided? Is there a benevolent dart-tator for life?)

DartBot avatar Feb 01 '14 22:02 DartBot

Set owner to @gbracha. Added Accepted label.

gbracha avatar Aug 27 '14 00:08 gbracha

I see that task has been assigned to someone... but it's been a year since the last activity... my question is... how much longer we have to wait for this syntax?

gil0mendes avatar Sep 16 '15 21:09 gil0mendes

Someone will need to create a dart enhancement proposal for it first.

seaneagan avatar Sep 17 '15 16:09 seaneagan

Supporting variable numbers of arguments would improve console logging methods in dart:html and for Flutter.

jacob314 avatar Jun 27 '18 19:06 jacob314

Is it possible that this feature will be included into the Dart 2.0?

roman-vanesyan avatar Jun 28 '18 18:06 roman-vanesyan

No, it is not planned (or started)

matanlurey avatar Jun 28 '18 19:06 matanlurey

Its almost essential, since dart doesn't inherit constructors.

Extending a class requires one to look up the full constructor of parent.

For me, that's just too much typing.

Like, this

class CustomPageRoute<T> extends MaterialPageRoute<T> {
  CustomPageRoute(*args, **kwargs) : super(*args, **kwargs);
}

compared to this

class CustomPageRoute<T> extends MaterialPageRoute<T> {
  CustomPageRoute({
    @required builder,
    RouteSettings settings,
    maintainState: true,
    bool fullscreenDialog: false,
  }): super(
          builder: builder,
          settings: settings,
          maintainState: maintainState,
          fullscreenDialog: fullscreenDialog,
        );
}

I get the reasons behind not inheriting constructors. Explicit is better than implicit. But *args, **kwargs is a great way to prevent excessive typing.

devxpy avatar Jul 14 '18 15:07 devxpy

@devxpy There were discussions about improving constructors, but as distinct topic, but most improvements are planned after Dart 2. Dart 2 was mostly about moving to common frontend (CFE) and sound type system.

zoechi avatar Jul 15 '18 16:07 zoechi

issue opened on 23 Jan 2014

hereisderek avatar Aug 03 '18 14:08 hereisderek

Question to the original reporters of this issue: Given that a variable number of arguments of the same type – ex. List<String> *args Python style or List<String> ...args JS style – wouldn't be too useful, how do you imagine expressing their types? List<Object> ...args on the other hand forces the function to forego static typing.

What is the prior art on statically typed varargs?

vp2177 avatar Dec 30 '18 04:12 vp2177

Any news on this? I'm working on a "ramda like" library and the lack of varargs is really limiting :/

simc avatar May 31 '19 22:05 simc

Hi, I just discovered a use case where I think using arguments unpacking to be the most elegant solution. However, I'm new to Dart so I might be wrong.

I have a function in Dart which has some optional arguments

List<num> arange(num start, [num end, num step = 1]) {
  if (end == null) {
    end = start;
    start = 0;
  }
  int length = (end - start) ~/ step;
  return List<num>.generate(length, (i) => start + i * step);
}

I want to call this function from Javascript, so I wrote my main as this:

import 'dart:js';

void main() {
  context['arange'] = arange;
}

However, when called from Javascript, this returns a Dart object, while what I want was a Javascript array. So the solution would be to use JsObject.jsify to convert the Dart list into a Javascript array.

If argument unpacking was available the implementation would be a simple one-liner edit using anonymous functions:

context['arange'] = (varargs) => JsObject.jsify(arange(*varargs));

Instead, without it, I think that I would need to write another function with the same signature as the function I want to convert, for each function I want to expose in this way.

JsObject arangeJS(num start, [num end, num step = 1]) {
  return JsObject.jsify(arange(start, end, step));
}

void main() {
  context['arange'] = arangeJS;
}

I might be missing something, as I'm new to the language, but this is the best I could come out with. Please feel free to comment or point any mistake.

Full main.dart:

import 'dart:js';

void main() {
  context['arange'] = arangeJS;
}

List<num> arange(num start, [num end, num step = 1]) {
  if (end == null) {
    end = start;
    start = 0;
  }
  int length = (end - start) ~/ step;
  return List<num>.generate(length, (i) => start + i * step);
}

JsObject arangeJS(num start, [num end, num step = 1]) {
  return JsObject.jsify(arange(start, end, step));
}

Compiled with dart2js -O2 -o arange.js main.dart --packages=.packages.

fcole90 avatar Jun 29 '19 10:06 fcole90

6 years and still no var args? 😀

vcraescu avatar Dec 29 '19 06:12 vcraescu

I am also looking forward to this. Made some memoization methods and had to write one method for each arg count, up to 4. Really cumbersome and breaks DRY principles...

mateusfccp avatar Jan 09 '20 19:01 mateusfccp

Variadic arguments are going to be very helpful, and make Prototyping as well as Writing code Easier.

If both C# and JS have them then that means that Dart which sits in between is starting to fall behind

CEbbinghaus avatar Feb 27 '20 10:02 CEbbinghaus

Waiting for this.

LukaGiorgadze avatar May 25 '20 13:05 LukaGiorgadze

This would be really useful for utility functions which need to be versatile.

For example:

Function debounce(Function func, int milliseconds) {
  Timer timer;
  return (arg) {
    if (timer != null) {
      timer.cancel();
    }

    timer = Timer(Duration(milliseconds: milliseconds), () => func(arg));
  };
}

Which unfortunately can be used only with fixed arguments.

As @seaneagan pointed out, something similar to rest parameters in JavaScript would be very useful:

Function debounce(Function func, int milliseconds) {
  Timer timer;
  return (...arg) {
    if (timer != null) {
      timer.cancel();
    }

    timer = Timer(Duration(milliseconds: milliseconds), () => func(...arg));
  };
}

omahili avatar Jun 07 '20 14:06 omahili

I'm not sure if varags would solve the forwarding case or not. For another discussion around wrapping functions of unknown arity and argument type see https://github.com/dart-lang/language/issues/157

natebosch avatar Jun 08 '20 20:06 natebosch

Please add this to Dart. see: https://stackoverflow.com/questions/3941517/converting-list-to-args-when-calling-function

ali-1989 avatar Aug 28 '20 08:08 ali-1989

I have some code like the following.

import 'dart:async';

typedef throttle_debounce_callback = dynamic Function(List<dynamic>? positionalArguments,
    [Map<Symbol, dynamic>? namedArguments]);

throttle_debounce_callback throttle(Function callback, int interval) {
  var enableCall = true;
  return (List<dynamic>? positionalArguments, [Map<Symbol, dynamic>? namedArguments]) {
    if (!enableCall) return;
    enableCall = false;
    Function.apply(callback, positionalArguments, namedArguments);
    Future.delayed(Duration(milliseconds: interval), () => enableCall = true);
  };
}

throttle_debounce_callback debounce(Function callback, int interval) {
  Timer? debounceTimeoutId;
  return (List<dynamic>? positionalArguments, [Map<Symbol, dynamic>? namedArguments]) {
    debounceTimeoutId?.cancel();
    debounceTimeoutId =
        Timer(Duration(milliseconds: interval), () => Function.apply(callback, positionalArguments, namedArguments));
  };
}

However the shortcoming is that I need to pass positional arguments as a list, instead of variable length arguments. I hope I can write code like this.

import 'dart:async';

typedef throttle_debounce_callback = dynamic Function([dynamic ...positionalArguments],
    [Map<Symbol, dynamic>? namedArguments]);

throttle_debounce_callback throttle(Function callback, int interval) {
  var enableCall = true;
  return ([dynamic ...positionalArguments], [Map<Symbol, dynamic>? namedArguments]) {
    if (!enableCall) return;
    enableCall = false;
    Function.apply(callback, positionalArguments, namedArguments);
    Future.delayed(Duration(milliseconds: interval), () => enableCall = true);
  };
}

throttle_debounce_callback debounce(Function callback, int interval) {
  Timer? debounceTimeoutId;
  return ([dynamic ...positionalArguments], [Map<Symbol, dynamic>? namedArguments]) {
    debounceTimeoutId?.cancel();
    debounceTimeoutId =
        Timer(Duration(milliseconds: interval), () => Function.apply(callback, positionalArguments, namedArguments));
  };
}

This would be more user-friendly.

Now the test code need to be

import 'dart:async';

import 'package:simple_throttle_debounce/simple_throttle_debounce.dart';

void main() async {
  var limit = 100;
  var tick = 0;
  var interval = 1000;
  var simpleTask = (tick) => print('tick: $tick');
  var throttleSimpleTask = throttle(simpleTask, interval);
  var debounceSimpleTask = debounce(simpleTask, interval);
  while (true) {
    print(tick);
    throttleSimpleTask([tick]);
    // debounceSimpleTask([tick]);
    await Future.delayed(Duration(milliseconds: 100), () => tick++);
    if (tick > limit) break;
  }
}

liudonghua123 avatar Sep 16 '20 03:09 liudonghua123

To add an example of usefulness of this : There was a request on to add the index to list.forEach in the dart language. Unfortunately it couldn't be done because dart doesn't allow to pass more arguments than the function can take in.

Imo this is kinda related to varargs where you should be able to pass more arguments than you need to, to a function.

cedvdb avatar Oct 28 '20 22:10 cedvdb

Good point cedvdb, then non-vararg functions could also have static type safety with spread arguments as long as all the rest of the parameters are optional and the same type as the list

Also, the hash functions of quiver could benefit from varags, then it could be reduced to a single hash function instead of hash2, hash3, hash4, and hashObjects if you have more - I'd like to suggest that a list of the same type as the varags could also be passed to the function and act like a positional argument, that way you don't need to explicitly spread it just to become a list within the function again (possible performance improvement? wouldn't create a new list, whereas spread args i'd imagine creates a new list?)

sodiboo avatar Nov 04 '20 18:11 sodiboo