www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Typesafe variadics in any position

reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
Having a conversation about the design of the new std.process, I lamented  
that typesafe variadics must be the last element in the list of parameters.

But I would love to have a version that simply takes parameters as  
parameters.  In Tango's process class, it was a nice feature.  Stuff like  
this just worked:

setArgs("prog.exe", "arg1", "arg2");

setArgs("prog.exe arg1 arg2".split());

All with one function.

Because spawnProcess has optional parameters at the end, it makes it  
impossible to have this, since typesafe variadics requires that the  
variadic be the last part of the function parameters, and spawnProcess  
must take the optional stream and config parameters last.

But why?  This seems perfectly plausible to me:

spawnProcess(string progname, string[] args..., File _stdin = stdin, File  
_stdout = stdout, File _stderr = stderr, Config config = Config.none);

There is no ambiguity with something like:

spawnProcess("prog.exe", "arg1", "arg2", File("infile.txt"));

A string does not implicitly convert to a File, and vice versa.

Typesafe variadics are actually extremely simple to deal with (much  
simpler than C-style or D-style variadics), it's just passed as a simple D  
slice.  The compiler takes care of the nasty parts, and is entirely  
call-side.

Basically, in order for this to work, a typesafe variadic parameter must  
have no way to implicitly convert to or from the type of the next  
parameters, up to the first non-variadic parameter.

In other words:

void foo(T[] arg1..., U[] arg2..., V arg3, W[] arg4...)

This works if T, U and V cannot be implicitly converted to each other.  W  
can implicitly convert to or from V, because V is positional (there MUST  
be one V argument in this call, and that matches arg3, so any args after  
that are assumed to be arg4)

Would this work? Would it be something people want?

-Steve
Feb 26 2013
next sibling parent "Andrej Mitrovic" <andrej.mitrovich gmail.com> writes:
On Tuesday, 26 February 2013 at 20:29:51 UTC, Steven 
Schveighoffer wrote:
 Would it be something people want?

Yes. Also related: http://d.puremagic.com/issues/show_bug.cgi?id=8687 One extremely common and important use-case of allowing it is this: foo(string[] args..., string file = __FILE__, size_t line = __LINE__); Right now as soon as you introduce variadics you can forget about using __FILE__/__LINE__ at runtime (unless you resort to some inner-template tricks, but that only works in some cases methinks, probably not at runtime).
Feb 26 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Feb 26, 2013 at 03:29:51PM -0500, Steven Schveighoffer wrote:
 Having a conversation about the design of the new std.process, I
 lamented that typesafe variadics must be the last element in the
 list of parameters.

 But why?  This seems perfectly plausible to me:
 
 spawnProcess(string progname, string[] args..., File _stdin = stdin,
 File _stdout = stdout, File _stderr = stderr, Config config =
 Config.none);

 Basically, in order for this to work, a typesafe variadic parameter
 must have no way to implicitly convert to or from the type of the
 next parameters, up to the first non-variadic parameter.
 
 In other words:
 
 void foo(T[] arg1..., U[] arg2..., V arg3, W[] arg4...)
 
 This works if T, U and V cannot be implicitly converted to each
 other.  W can implicitly convert to or from V, because V is
 positional (there MUST be one V argument in this call, and that
 matches arg3, so any args after that are assumed to be arg4)
 
 Would this work? Would it be something people want?

+1, yes!!! This would also make it possible to do things like user-defined library types that throw exception at the caller by using __FILE__ and __LINE__, when the function is variadic. Currently, there is no way to do this: void func(MyType args..., string file=__FILE__, size_t line=__LINE__) { dotDotDotMagic(args); if (failed) throw new Exception(file, line, epicFailMsg); } Well, you could put file and line as compile-time parameters, but then you'll get extreme template bloat. Nor, for that matter, this: void func(T...)(string file=__FILE__, size_t line=__LINE__, MyType target, T args) { ... } Which, ideally, would do the right thing when invoked like this: MyType obj1, obj2; func(obj2, 1, 2, 3, 'a', 'c', "c"); func(obj2, "abc", 'd', 'e', 'f', 123); In both cases there is no ambiguity, because MyType does not implicitly convert to string or size_t, so, in theory, the compiler should be able to figure out what the user intended. T -- MAS = Mana Ada Sistem?
Feb 26 2013
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 26 Feb 2013 15:36:11 -0500, Andrej Mitrovic  
<andrej.mitrovich gmail.com> wrote:

 On Tuesday, 26 February 2013 at 20:29:51 UTC, Steven Schveighoffer wrote:
 Would it be something people want?

Yes. Also related: http://d.puremagic.com/issues/show_bug.cgi?id=8687 One extremely common and important use-case of allowing it is this: foo(string[] args..., string file = __FILE__, size_t line = __LINE__); Right now as soon as you introduce variadics you can forget about using __FILE__/__LINE__ at runtime (unless you resort to some inner-template tricks, but that only works in some cases methinks, probably not at runtime).

Well, except that example wouldn't work :) This would though: foo(string[] args..., size_t line = __LINE__, string file = __FILE__); Excellent use case though! -Steve
Feb 26 2013
prev sibling next sibling parent "simendsjo" <simendsjo gmail.com> writes:
On Tuesday, 26 February 2013 at 20:52:46 UTC, Steven 
Schveighoffer wrote:
 On Tue, 26 Feb 2013 15:36:11 -0500, Andrej Mitrovic 
 <andrej.mitrovich gmail.com> wrote:

 On Tuesday, 26 February 2013 at 20:29:51 UTC, Steven 
 Schveighoffer wrote:
 Would it be something people want?

Yes. Also related: http://d.puremagic.com/issues/show_bug.cgi?id=8687 One extremely common and important use-case of allowing it is this: foo(string[] args..., string file = __FILE__, size_t line = __LINE__); Right now as soon as you introduce variadics you can forget about using __FILE__/__LINE__ at runtime (unless you resort to some inner-template tricks, but that only works in some cases methinks, probably not at runtime).

Well, except that example wouldn't work :) This would though: foo(string[] args..., size_t line = __LINE__, string file = __FILE__); Excellent use case though! -Steve

or use a new struct struct SourcePos { string file; size_t line; } foo(string[] args..., SourcePos pos = SourcePos(__FILE__, __LINE__));
Feb 26 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Feb 26, 2013 at 09:59:22PM +0100, simendsjo wrote:
 On Tuesday, 26 February 2013 at 20:52:46 UTC, Steven Schveighoffer
 wrote:
On Tue, 26 Feb 2013 15:36:11 -0500, Andrej Mitrovic
<andrej.mitrovich gmail.com> wrote:

On Tuesday, 26 February 2013 at 20:29:51 UTC, Steven
Schveighoffer wrote:



foo(string[] args..., string file = __FILE__, size_t line =
__LINE__);

Right now as soon as you introduce variadics you can forget about
using __FILE__/__LINE__ at runtime (unless you resort to some
inner-template tricks, but that only works in some cases methinks,
probably not at runtime).

Well, except that example wouldn't work :) This would though: foo(string[] args..., size_t line = __LINE__, string file = __FILE__); Excellent use case though!


 or use a new struct
 struct SourcePos {
   string file;
   size_t line;
 }
 foo(string[] args..., SourcePos pos = SourcePos(__FILE__, __LINE__));

Excellent idea!!! Why didn't I think of this before... this would help even with the current limitation on variadics, as it helps solve ambiguity problems with overloaded functions: void func(string a, string file=__FILE__, int line=__LINE__); void func(string a, string b, string file=__FILE__, int line=__LINE__); func("a"); func("a", "b"); // ambiguity error Encapsulating it in SourcePos avoids the ambiguity problem. But of course, having variadics support this would make it so much more useful. T -- It is not the employer who pays the wages. Employers only handle the money. It is the customer who pays the wages. -- Henry Ford
Feb 26 2013
prev sibling next sibling parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Tuesday, 26 February 2013 at 20:29:51 UTC, Steven 
Schveighoffer wrote:
 Having a conversation about the design of the new std.process, 
 I lamented that typesafe variadics must be the last element in 
 the list of parameters.

Could the idea be expanded into allowing optional arguments in front of non-optional ones, when it is not ambiguous to do so?
Feb 26 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/26/13 5:29 PM, H. S. Teoh wrote:
 On Tue, Feb 26, 2013 at 05:16:29PM -0500, Steven Schveighoffer wrote:
 On Tue, 26 Feb 2013 17:04:40 -0500, timotheecour
 <thelastmammoth gmail.com>  wrote:

 I proposed this a few weeks ago, but nobody gave feedback. This
 would enable it without worrying about special cases.

 http://forum.dlang.org/thread/miqtvuufvlwgfzblexxp forum.dlang.org
 feature request: special optional argument (__FILE__, ...) AFTER
 variadic template.

I think this is a different problem, the issue I'm talking about is typesafe variadics, not variadic templates.
 Please let me know what you think!

Doesn't this work? foo(string filename=__FILE__, T...)(T args)

It should work, except that you'll have template bloat (not as bad as putting __line__ in the compile-time arguments though!)

Just forward immediately to a regular function. That gets rid of the bloat. Andrei
Feb 26 2013
prev sibling next sibling parent "timotheecour" <thelastmammoth gmail.com> writes:
I proposed this a few weeks ago, but nobody gave feedback. This 
would enable it without worrying about special cases.

http://forum.dlang.org/thread/miqtvuufvlwgfzblexxp forum.dlang.org
feature request: special optional argument (__FILE__, ...) AFTER 
variadic template.

Please let me know what you think!
Feb 26 2013
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 26 Feb 2013 16:38:39 -0500, Vladimir Panteleev  
<vladimir thecybershadow.net> wrote:

 On Tuesday, 26 February 2013 at 20:29:51 UTC, Steven Schveighoffer wrote:
 Having a conversation about the design of the new std.process, I  
 lamented that typesafe variadics must be the last element in the list  
 of parameters.

Could the idea be expanded into allowing optional arguments in front of non-optional ones, when it is not ambiguous to do so?

I think so, technically, it's no different than using a variadic parameter, and then requiring exactly none or one element in the variadic argument spot. The one dangerous thing I thought of is we currently allow two consecutive same-type optional parameters. The issue there is if a type changes, a call to the function might silently bind different parameters. So perhaps we would have to stick to situations that aren't already valid (such as your idea of having an optional parameter before a required one), and disallow functions to have consecutive like-typed optional parameters in those cases. Not sure if it's worth it. It would be confusing to allow consecutive like-typed optional parameters at the end, but not at the front. -Steve
Feb 26 2013
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 26 Feb 2013 17:04:40 -0500, timotheecour  
<thelastmammoth gmail.com> wrote:

 I proposed this a few weeks ago, but nobody gave feedback. This would  
 enable it without worrying about special cases.

 http://forum.dlang.org/thread/miqtvuufvlwgfzblexxp forum.dlang.org
 feature request: special optional argument (__FILE__, ...) AFTER  
 variadic template.

I think this is a different problem, the issue I'm talking about is typesafe variadics, not variadic templates.
 Please let me know what you think!

Doesn't this work? foo(string filename=__FILE__, T...)(T args) -Steve
Feb 26 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Feb 26, 2013 at 10:38:39PM +0100, Vladimir Panteleev wrote:
 On Tuesday, 26 February 2013 at 20:29:51 UTC, Steven Schveighoffer
 wrote:
Having a conversation about the design of the new std.process, I
lamented that typesafe variadics must be the last element in the
list of parameters.

Could the idea be expanded into allowing optional arguments in front of non-optional ones, when it is not ambiguous to do so?

+1. T -- What do you get if you drop a piano down a mineshaft? A flat minor.
Feb 26 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Feb 26, 2013 at 05:16:29PM -0500, Steven Schveighoffer wrote:
 On Tue, 26 Feb 2013 17:04:40 -0500, timotheecour
 <thelastmammoth gmail.com> wrote:
 
I proposed this a few weeks ago, but nobody gave feedback. This
would enable it without worrying about special cases.

http://forum.dlang.org/thread/miqtvuufvlwgfzblexxp forum.dlang.org
feature request: special optional argument (__FILE__, ...) AFTER
variadic template.

I think this is a different problem, the issue I'm talking about is typesafe variadics, not variadic templates.
Please let me know what you think!

Doesn't this work? foo(string filename=__FILE__, T...)(T args)

It should work, except that you'll have template bloat (not as bad as putting __line__ in the compile-time arguments though!) T -- What are you when you run out of Monet? Baroque.
Feb 26 2013
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 26 Feb 2013 17:29:10 -0500, H. S. Teoh <hsteoh quickfur.ath.cx>  
wrote:

 On Tue, Feb 26, 2013 at 05:16:29PM -0500, Steven Schveighoffer wrote:
 On Tue, 26 Feb 2013 17:04:40 -0500, timotheecour
 <thelastmammoth gmail.com> wrote:

I proposed this a few weeks ago, but nobody gave feedback. This
would enable it without worrying about special cases.

http://forum.dlang.org/thread/miqtvuufvlwgfzblexxp forum.dlang.org
feature request: special optional argument (__FILE__, ...) AFTER
variadic template.

I think this is a different problem, the issue I'm talking about is typesafe variadics, not variadic templates.
Please let me know what you think!

Doesn't this work? foo(string filename=__FILE__, T...)(T args)

It should work, except that you'll have template bloat (not as bad as putting __line__ in the compile-time arguments though!)

Well, not too bad. The template can be a wrapper call, and would likely be inlined. But the symbol bloat would suck... -Steve
Feb 26 2013
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-02-26 21:29, Steven Schveighoffer wrote:
 Having a conversation about the design of the new std.process, I
 lamented that typesafe variadics must be the last element in the list of
 parameters.

 But I would love to have a version that simply takes parameters as
 parameters.  In Tango's process class, it was a nice feature.  Stuff
 like this just worked:

 setArgs("prog.exe", "arg1", "arg2");

 setArgs("prog.exe arg1 arg2".split());

 All with one function.

 Because spawnProcess has optional parameters at the end, it makes it
 impossible to have this, since typesafe variadics requires that the
 variadic be the last part of the function parameters, and spawnProcess
 must take the optional stream and config parameters last.

 But why?  This seems perfectly plausible to me:

 spawnProcess(string progname, string[] args..., File _stdin = stdin,
 File _stdout = stdout, File _stderr = stderr, Config config = Config.none);

 There is no ambiguity with something like:

 spawnProcess("prog.exe", "arg1", "arg2", File("infile.txt"));

 A string does not implicitly convert to a File, and vice versa.

 Typesafe variadics are actually extremely simple to deal with (much
 simpler than C-style or D-style variadics), it's just passed as a simple
 D slice.  The compiler takes care of the nasty parts, and is entirely
 call-side.

 Basically, in order for this to work, a typesafe variadic parameter must
 have no way to implicitly convert to or from the type of the next
 parameters, up to the first non-variadic parameter.

 In other words:

 void foo(T[] arg1..., U[] arg2..., V arg3, W[] arg4...)

 This works if T, U and V cannot be implicitly converted to each other.
 W can implicitly convert to or from V, because V is positional (there
 MUST be one V argument in this call, and that matches arg3, so any args
 after that are assumed to be arg4)

 Would this work? Would it be something people want?

 -Steve

I like. But in the case of spawnProcess I would prefer named parameters: spawnProcess("prog.exe", "arg1", "arg2", _stdin: File("infile.txt")); -- /Jacob Carlborg
Feb 27 2013