Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Variable Number of Arguments #1014

Open
DartBot opened this issue Jan 23, 2014 · 49 comments
Open

Feature Request: Variable Number of Arguments #1014

DartBot opened this issue Jan 23, 2014 · 49 comments
Labels
request Requests to resolve a particular developer problem

Comments

@DartBot
Copy link

DartBot commented Jan 23, 2014

This issue was originally filed by ador...@gmail.com


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];
  ...
}
@sgjesse
Copy link

sgjesse commented Jan 23, 2014

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

@DartBot
Copy link
Author

DartBot commented Jan 23, 2014

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
Copy link
Author

DartBot commented Jan 23, 2014

This comment was originally written by adors...@gmail.com


+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
Copy link
Author

DartBot commented Feb 1, 2014

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
Copy link
Author

DartBot commented Feb 1, 2014

This comment was originally written by ado...@gmail.com


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
Copy link
Author

DartBot commented Feb 1, 2014

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
Copy link
Author

DartBot commented Feb 1, 2014

This comment was originally written by yu.asa...@gmail.com


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
Copy link
Author

DartBot commented Feb 1, 2014

This comment was originally written by ado...@gmail.com


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?)

@gbracha
Copy link

gbracha commented Aug 27, 2014

Set owner to @gbracha.
Added Accepted label.

@gil0mendes
Copy link

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?

@seaneagan
Copy link

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

@jacob314
Copy link
Member

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

@roman-vanesyan
Copy link

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

@matanlurey
Copy link
Contributor

No, it is not planned (or started)

@devxpy
Copy link

devxpy commented Jul 14, 2018

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.

@zoechi
Copy link

zoechi commented Jul 15, 2018

@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.

@hereisderek
Copy link

issue opened on 23 Jan 2014

@vp2177
Copy link

vp2177 commented Dec 30, 2018

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?

@simc
Copy link

simc commented May 31, 2019

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

@fcole90
Copy link

fcole90 commented Jun 29, 2019

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.

@vcraescu
Copy link

6 years and still no var args? 😀

@mateusfccp
Copy link
Contributor

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...

@CEbbinghaus
Copy link

CEbbinghaus commented Feb 27, 2020

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

@LukaGiorgadze
Copy link

Waiting for this.

@omahili
Copy link

omahili commented Jun 7, 2020

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));
  };
}

@natebosch natebosch transferred this issue from dart-lang/sdk Jun 8, 2020
@natebosch
Copy link
Member

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 #157

@lrhn lrhn added the request Requests to resolve a particular developer problem label Jun 16, 2020
@ali-1989
Copy link

@liudonghua123
Copy link

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;
  }
}

@cedvdb
Copy link

cedvdb commented Oct 28, 2020

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.

@sodiboo
Copy link

sodiboo commented Nov 4, 2020

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?)

@sae13

This comment has been minimized.

@esenmx
Copy link

esenmx commented Jul 14, 2021

I think multiple assignment is also related to this feature. Both args and multiple assignment can reduce significantly amount of code written in many cases.

@Levi-Lesches
Copy link

For those saying they need this feature, can you elaborate on why using a List won't work? In cases where all the arguments are of the same type, you can use List<int> or another type, and when they're not, you'd use List<Object> or just List. Either way, you lose type safety, so one isn't worse than the other.

@esenmx
Copy link

esenmx commented Jul 15, 2021

For those saying they need this feature, can you elaborate on why using a List won't work? In cases where all the arguments are of the same type, you can use List<int> or another type, and when they're not, you'd use List<Object> or just List. Either way, you lose type safety, so one isn't worse than the other.

It's mostly about code quality.
Very very simple example:

final a = 42;
final b = 90;

void argsFn(...int args) {};
void arrayFn(List<int> array) {};

main() {
  argsFn(a, b);
  arrayFn(<int>[a, b]);
};

I would choose argsFn all time...

Also don't forget SQL has variadic parameters and using array won't work for the SQL code generators where args needed because arrays and args are fundamentally different things. So it's literally necessity after all.

Edit: Workarounds are always possible but I'm and most of you as I guess not big fan of language of workarounds...

@ykmnkmi
Copy link

ykmnkmi commented Jul 15, 2021

Is a dart way to use different method for collections/arguments, like add/addAll, build/buildAll, ....
Easy way to implement this is transforming argsFn(varargs int args) to argsFn(List<int> args) and replace calls with list in CFE.

@Levi-Lesches
Copy link

final a = 42;
final b = 90;

void argsFn(...int args) {};
void arrayFn(List<int> array) {};

main() {
  argsFn(a, b);
  arrayFn(<int>[a, b]);
};

Except if a and b are known to be ints, which they have to be in order to be passed to argsFn, then you don't need to include <int> around the brackets, so the code ends up being just as short:

final a = 42;
final b = 90;

void fn1(...int args) {}
void fn2(List<int> array) {}

void main() {
  fn2(a, b);
  fn2([a, b]);
}

As for code generators, well those can be considered workarounds in and of themselves, although I don't use them as much so I can't really sympathize. To clarify, I do think this would be a neat feature (if all the var-args were of the same type and collapsed to a List<E>), I was just trying to gauge how high of a priority this should be when there are other high-profile issues going around.

@mmcdon20
Copy link

If dart ever does implement varargs, it would be nice if it supported using the equivalent of collection if and collection for at the call site, otherwise it would seem less expressive than just accepting a collection literal.

void argsFn(...int args) {}

void main() {
  argsFn(
    for (int i = 0; i < 10; i++)
      if (i % 2 == 0) i,
  );
}

Additionally, I think it would be nice if you weren't limited to only collecting the arguments into a List<A>. Why not allow varargs to collect to a Set<A> or Map<A, B> or Iterable<A> or even custom user defined data structures?

void varArgsList(...List<int> args) {}
void varArgsSet(...Set<int> args) {}
void varArgsMap(...Map<int, int> args) {}
void varArgsIterable(...Iterable<int> args) {}
void varArgsCustom(...Custom<int> args) {}

A rough idea for how this might work, is that perhaps classes with an .of constructor can be used with varargs. If the constructor accepts an Iterable<A> like with List<A>.of and Set<A>.of then the arguments are collected into an Iterable<A> and passed to the .of constructor. If the .of constructor accepts a Map<A. B> instead, like with Map<A, B>.of, then at the callsite you would pass in the varargs as key/value pairs.

Iterable itself, does not have an .of constructor, but perhaps a special case could be made for the Iterable type, which would ideally collect the arguments as a lazy iterable (like a sync* function).

Basically this:

void varArgsList(...List<int> args) {}
void varArgsSet(...Set<int> args) {}
void varArgsMap(...Map<int, int> args) {}
void varArgsIterable(...Iterable<int> args) {}
void varArgsCustom(...Custom<int> args) {}

void main() {
  varArgsList(for (int i = 0; i < 10; i++) if (i % 2 == 0) i);
  varArgsSet(for (int i = 0; i < 10; i++) if (i % 2 == 0) i);
  varArgsMap(for (int i = 0; i < 10; i++) if (i % 2 == 0) i: i);
  varArgsIterable(for (int i = 0; i < 10; i++) if (i % 2 == 0) i);
  varArgsCustom(for (int i = 0; i < 10; i++) if (i % 2 == 0) i);
}

Could be syntactic sugar for this:

void varArgsList(List<int> args) {}
void varArgsSet(Set<int> args) {}
void varArgsMap(Map<int, int> args) {}
void varArgsIterable(Iterable<int> args) {}
void varArgsCustom(Custom<int> args) {}

void main() {
  varArgsList(List<int>.of(() sync* {
    for (int i = 0; i < 10; i++) if (i % 2 == 0) yield i;
  }()));
  varArgsSet(Set<int>.of(() sync* {
    for (int i = 0; i < 10; i++) if (i % 2 == 0) yield i;
  }()));
  varArgsMap(Map<int, int>.of({
    for (int i = 0; i < 10; i++)
      if (i % 2 == 0) i: i,
  }));
  varArgsIterable(() sync* {
    for (int i = 0; i < 10; i++) if (i % 2 == 0) yield i;
  }());
  varArgsCustom(Custom<int>.of(() sync* {
    for (int i = 0; i < 10; i++) if (i % 2 == 0) yield i;
  }()));
}

@ykmnkmi
Copy link

ykmnkmi commented Dec 28, 2021

@mmcdon20 too complicated and why not T ...name:

int sum(int ...args);
int sumFn(Iterable<int> args);

also there are other issues for maps and objects.

@mmcdon20
Copy link

too complicated

@ykmnkmi which part is too complicated?

To summarize, I suggested 2 things:

  1. varargs should allow if and for at the call site.
  2. You should be able to pick a data structure to collect the arguments into.

If you think about it, dart already has varargs, its just limited to collection literals. And collection literals support the above features.

As for suggestion 1, it's pretty much a given that if dart adds varargs, it will support spreads ... at the call site. So why not for and if as well?

As for suggestion 2, I admit I expected this suggestion would be a bit more controversial. I can't think of any other programming language that allows you to do this, so it would be going against the norm. But it would add some more flexibility compared to only collecting the arguments as a List<T> like other programming languages do.

and why not T ...name

I don't have a preference between T ...name and ...T name.

@munificent
Copy link
Member

As for suggestion 1, it's pretty much a given that if dart adds varargs, it will support spreads ... at the call site. So why not for and if as well?

Yeah, if we do varargs, I certainly want to push to support all three of those.

@Policy56

This comment was marked as off-topic.

@Sunbreak
Copy link

Sunbreak commented Apr 1, 2022

Any progress on this? It is blocking dart-lang/sdk#38578

@munificent
Copy link
Member

No progress yet. We're mostly focused on static metaprogramming, views, and pattern matching.

@jodinathan
Copy link

this could be also used with js_bindings to have nice interoped variadic methods with JS

@SzczurekYT
Copy link

We really need this. and dart is falling behind many other laguages. Java, Javascript, Python, C# all these langages have varargs and dart doesn't. I know that dart is pretty new, laguage but It would be really cool if we had this.

I hope that someona will finally implement this.

@sarankumar-ns
Copy link

9 years and counting...

In my case, I need to pass varargs in JS Interop function. It expects varargs instead of List.

@Sunbreak
Copy link

9 years and counting...

In my case, I need to pass varargs in JS Interop function. It expects varargs instead of List.

dart:ffi: Support variadic C functions is closed

I suppose dart:ffi or dart2wasm have more priority

@Sunbreak
Copy link

9 years and counting...

In my case, I need to pass varargs in JS Interop function. It expects varargs instead of List.

BTW, you could try fixed arguments instead of List: dart-lang/sdk#38578 (comment)

Or @staticInterop: flutter/flutter#97357 (comment) and https://github.com/dart-lang/site-www/pull/4528/files

Or comment on dart-lang/sdk#35084

@bernaferrari
Copy link

I'm not sure this will ever be implemented, but with Records someone could make a typeDef vararg = record and inline (()) into ()

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
request Requests to resolve a particular developer problem
Projects
None yet
Development

No branches or pull requests