Thread overview | |||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
May 28, 2015 As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
I might do a blog post on this, but here's some POC code: import std.stdio; import std.range; import std.typetuple; import std.traits; import std.conv; struct Foo { int i; } struct Bar { int i; } struct Baz { int i; } void func(Foo foo, Bar bar, Baz baz) { writeln("foo is ", foo); writeln("bar is ", bar); writeln("baz is ", baz); } auto getStrArgs(alias F, T...)() { string[] strArgs; foreach(i, ParamType; ParameterTypeTuple!F) { enum index = staticIndexOf!(ParamType, T); static if(index != -1) { strArgs ~= "args[" ~ index.to!string ~ "]"; } else { strArgs ~= ParamType.stringof ~ ".init"; } } return strArgs; } auto kwargs(alias F, T...)(T args) { enum strArgs = getStrArgs!(F, T); mixin("return F(" ~ strArgs.join(",") ~ ");"); } void main() { kwargs!func(Bar(2), Baz(3), Foo(1)); kwargs!func(Baz(3), Foo(1)); } |
May 29, 2015 Re: As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Atila Neves | On 2015-05-29 00:35, Atila Neves wrote: > I might do a blog post on this, but here's some POC code: > > import std.stdio; > import std.range; > import std.typetuple; > import std.traits; > import std.conv; > > > struct Foo { int i; } > struct Bar { int i; } > struct Baz { int i; } > > > void func(Foo foo, Bar bar, Baz baz) { > writeln("foo is ", foo); > writeln("bar is ", bar); > writeln("baz is ", baz); > } > > > auto getStrArgs(alias F, T...)() { > string[] strArgs; > > foreach(i, ParamType; ParameterTypeTuple!F) { > enum index = staticIndexOf!(ParamType, T); > > static if(index != -1) { > strArgs ~= "args[" ~ index.to!string ~ "]"; > } else { > strArgs ~= ParamType.stringof ~ ".init"; > } > } > > return strArgs; > } > > auto kwargs(alias F, T...)(T args) { > enum strArgs = getStrArgs!(F, T); > mixin("return F(" ~ strArgs.join(",") ~ ");"); > } > > void main() { > kwargs!func(Bar(2), Baz(3), Foo(1)); > kwargs!func(Baz(3), Foo(1)); > } Here's another solution [1]. And here's an implementation with language support which allows named arguments but not reordering the arguments [2]. Originally implemented by Michel Fortin. [1] https://github.com/jacob-carlborg/mambo/blob/master/mambo/util/Reflection.d#L135 [2] https://github.com/jacob-carlborg/dmd/tree/named_parameters -- /Jacob Carlborg |
May 29, 2015 Re: As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | I know some people don't like abusing opDollar, but here's my implementation: import std.traits, std.algorithm, std.conv; private struct Named(string n, T) { enum name = n; T value; } private auto makeNameIndex(NamedList...)(string[] names) { auto indices = new size_t[](names.length); foreach(i, n ; NamedList) { auto x = names.countUntil(n.name); assert(x >= 0, "Keyword Parameter \"" ~ n.name ~ "\" does not exist in " ~ text(names)); indices[x] = i; } return indices; } private immutable struct KeyWordDollar { @property auto opDispatch(string param, T)(T t) { return Named!(param,T)(t); } } private struct KeyWordManager(alias f) { private enum paramNames = [ParameterIdentifierTuple!f]; auto opDollar(size_t pos = 0)() const { return immutable KeyWordDollar(); } auto opIndex(NamedArgs...)(NamedArgs namedArgs) { enum ordering = makeNameIndex!NamedArgs(paramNames); ParameterTypeTuple!f args; foreach(i, ref a; args) { a = namedArgs[ ordering[i] ].value; } return f(args); } } auto kw(alias f)() { return KeyWordManager!f(); } int foo(int x, int y) { return x+y; } unittest { assert(8 == kw!foo[$.y = 5, $.x = 3]); } |
May 29, 2015 Re: As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Atila Neves | Two other ways to implement the concept. The first works with optional arguments and the other requires all arguments. /** * Optional arguments and no default values; might not compile if all args * not passed. */ --------------------------------------------------------------------------- /** * A templated function useful for creating aliases for naming function * parameters. */ auto namedArguments(string name, T)(T arguments) { static struct Args { enum string argName = name; T argValue; alias argValue this; } return Args(cast(Unqual!T)arguments); } /// unittest { alias arg1 = namedArguments!("arg1", string); alias arg2 = namedArguments!("arg2", int); void namedArgsFunc(T...)(T arguments) { const vars = parseArguments!()(arguments); const string one = vars.arg1; const int two = vars.arg2; } namedArgsFunc(arg1(""), arg2(42)); namedArgsFunc(arg2(56), arg1("fred")); } private enum isNamedArgument(T) = hasMember!(T, "argName") && hasMember! (T, "argValue"); /** * Parse parameters of the type returned from $(D namedArguments) into a * named tuple usable in a function. */ auto parseArguments(T...)(T inputs) if (allSatisfy!(isNamedArgument, T)) { template GetTypeAndName(ArgT) { alias Type = typeof(ArgT.argValue); enum string Name = ArgT.argName; alias GetTypeAndName = TypeTuple!(Type, Name); } auto ret = Tuple!(staticMap!(GetTypeAndName, T))(); foreach (arg; inputs) { mixin(`ret.` ~ arg.argName) = arg.argValue; } return ret; } unittest { alias arg1 = namedArguments!("arg1", string); alias arg2 = namedArguments!("arg2", int); const test1 = parseArguments(arg1("string"), arg2(42)); assert(test1.arg1 == "string"); assert(test1.arg2 == 42); const test2 = parseArguments(arg2(42), arg1("string")); assert(test2.arg1 == "string"); assert(test2.arg2 == 42); } -------------------------------------------------------------------------- ///All required arguments -------------------------------------------------------------------------- /** * A templated function useful for creating aliases for naming function * parameters. * * Params: * ArgTypesT = The enum type for all named parameters in a group. * * Returns: A voldemort type that can be handled by $(D parseArguments). */ template namedArguments(ArgTypesT) if (is(ArgTypesT == enum)) { auto namedArguments(ArgTypesT type, T)(T arguments) { static struct Args { alias ArgType = ArgTypesT; enum ArgType argsType = type; T args; alias args this; } return Args(cast(Unqual!T)arguments); } } /// unittest { enum ArgTypes { Arg1, Arg2 } alias FuncArgsT = namedArguments!ArgTypes; alias arg1 = FuncArgsT!(ArgTypes.Arg1, string); alias arg2 = FuncArgsT!(ArgTypes.Arg2, int); void namedArgsFunc(T...)(T arguments) { const vars = parseArguments(arguments); const string one = vars.Arg1; const int two = vars.Arg2; } namedArgsFunc(arg1(""), arg2(42)); namedArgsFunc(arg2(56), arg1("fred")); } private enum isNamedArgument(T) = hasMember!(T, "argsType") && hasMember! (T, "args"); /** * Parse parameters of the type returned from $(D namedArguments) into a * named tuple usable in a function. */ auto parseArguments(T...)(T inputs) if (allSatisfy!(isNamedArgument, T)) { template GetTypeAndName(ArgT) { import std.conv : to; alias Type = typeof(ArgT.args); enum string Name = ArgT.argsType.to!string(); alias GetTypeAndName = TypeTuple!(Type, Name); } auto ret = Tuple!(staticMap!(GetTypeAndName, T))(); foreach (I, arg; inputs) { ret[I] = arg.args; } return ret; } unittest { enum ArgTypes { Arg1, Arg2 } alias FuncArgsT = namedArguments!ArgTypes; alias arg1 = FuncArgsT!(ArgTypes.Arg1, string); alias arg2 = FuncArgsT!(ArgTypes.Arg2, int); const test1 = parseArguments(arg1("string"), arg2(42)); assert(test1.Arg1 == "string"); assert(test1.Arg2 == 42); const test2 = parseArguments(arg2(42), arg1("string")); assert(test2.Arg1 == "string"); assert(test2.Arg2 == 42); } -------------------------------------------------------------------------- Jonathan |
May 29, 2015 Re: As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Atila Neves | On Thursday, 28 May 2015 at 22:35:14 UTC, Atila Neves wrote: > I might do a blog post on this, but here's some POC code: Now with default values! import std.stdio; import std.range; import std.typetuple; import std.traits; import std.conv; struct Foo { int i; } struct Bar { int i; } struct Baz { int i; } void func(Foo foo = Foo(11), Bar bar = Bar(22), Baz baz = Baz(33)) { writeln("foo is ", foo); writeln("bar is ", bar); writeln("baz is ", baz); } void main() { kwargs!func(Bar(2), Baz(3), Foo(1)); //1, 2, 3 kwargs!func(Baz(3), Foo(1)); //1, 22, 3 } auto getStrArgs(alias F, T...)() { string[] strArgs; foreach(i, ParamType; ParameterTypeTuple!F) { enum index = staticIndexOf!(ParamType, T); static if(index != -1) { strArgs ~= "args[" ~ index.to!string ~ "]"; } else { alias defaultValue = ParameterDefaultValueTuple!F[i]; static if(is(defaultValue == void)) { strArgs ~= ParamType.stringof ~ ".init"; } else { strArgs ~= defaultValue.stringof; } } } return strArgs; } auto kwargs(alias F, T...)(T args) { enum strArgs = getStrArgs!(F, T); mixin("return F(" ~ strArgs.join(",") ~ ");"); } |
May 29, 2015 Re: As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan Crapuchettes | On Friday, 29 May 2015 at 15:16:56 UTC, Jonathan Crapuchettes wrote: > Two other ways to implement the concept. The first works with optional > arguments and the other requires all arguments. > > unittest > { > alias arg1 = namedArguments!("arg1", string); > alias arg2 = namedArguments!("arg2", int); > > void namedArgsFunc(T...)(T arguments) > { > const vars = parseArguments!()(arguments); > const string one = vars.arg1; > const int two = vars.arg2; > } > namedArgsFunc(arg1(""), arg2(42)); > namedArgsFunc(arg2(56), arg1("fred")); > } ... > Jonathan This is a very interesting approach, but the problem is that the person writing the function has to work to give support. I think we should try to create a wrapper function taking the original function as (alias F) that leverages ParameterIdentifierTuple , ParameterTypeTuple and ParameterDefaultValueTuple to generate the namedArguments automatically. Also, we could actually make this a Functor/Callback object that has already created members that give you easy access to the types of the named arguments. This way, we can even conceive a way to return a "partial" function with enough arguments, and only really call it when all the keyword arguments were actually provided (or enough of them were provided and the rest have provided a default value). This will allow us to create a concise API that is convenient, and strong. Liran |
May 30, 2015 Re: As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg Attachments: | On Fri, 29 May 2015 14:27:02 +0200, Jacob Carlborg wrote:
> [2] https://github.com/jacob-carlborg/dmd/tree/named_parameters
nice start. with some efforts i ported it as PoC to git HEAD, and added argument reordering, so
void foo (string a, string b);
...
foo(a:"hello", b:"world");
foo(b:"world", a:"hello");
is working as one expects.
nice addition to Aliced. ;-)
|
May 30, 2015 Re: As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg Attachments: | On Fri, 29 May 2015 14:27:02 +0200, Jacob Carlborg wrote: i can do even more cool things like this now: void test (string a, string b="wow", string c="heh") { assert(b == "wow"); } test(c: "cc", a: "aa"); the long-wanting feature of "use defaults for some args". i like it. |
May 31, 2015 Re: As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On 2015-05-29 12:27:02 +0000, Jacob Carlborg <doob@me.com> said: > And here's an implementation with language support which allows named arguments but not reordering the arguments [2]. Originally implemented by Michel Fortin. > > [2] https://github.com/jacob-carlborg/dmd/tree/named_parameters I didn't know you revived that thing too. Nice. Make sure you take note of the related comments between Walter and me here: https://github.com/michelf/dmd/commit/673bae4982ff18a3d216bc1578f50d40f4d26d7a At some point my plans about this changed and I wanted to implement named arguments differently so it'd work for template arguments too. But I never go to it. -- Michel Fortin michel.fortin@michelf.ca http://michelf.ca |
May 31, 2015 Re: As discussed in DConf2015: Python-like keyword arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin Attachments: | On Sat, 30 May 2015 21:59:53 -0400, Michel Fortin wrote: > On 2015-05-29 12:27:02 +0000, Jacob Carlborg <doob@me.com> said: > >> And here's an implementation with language support which allows named arguments but not reordering the arguments [2]. Originally implemented by Michel Fortin. >> >> [2] https://github.com/jacob-carlborg/dmd/tree/named_parameters > > I didn't know you revived that thing too. Nice. > > Make sure you take note of the related comments between Walter and me > here: > https://github.com/michelf/dmd/ commit/673bae4982ff18a3d216bc1578f50d40f4d26d7a > > At some point my plans about this changed and I wanted to implement named arguments differently so it'd work for template arguments too. But I never go to it. my work now allows this: string test (string a, string b="wow", string c="heh") { return a~b~c; } void main () { enum str = test(c: "cc", a: "aa"); assert(str == "aawowcc"); } and this: void test(A...) (A a) { import std.stdio; foreach (auto t; a) writeln(t); } void main () { test(x: 33.3, z: 44.4, a: 9999, 7777, d:"Yehaw"); } but still not this: string test(T) (T a, string b="wow", string c="heh") { import std.conv : to; return to!string(a)~b~c; } void main () { enum str = test(c: "cc", a: 42); // can't //enum str = test(a: 42, c: "cc"); // WORKS assert(str == "42wowcc"); } i have to add reorder checks to template resoultion code yet. also, no support for named "!" args. the patch is fairly simple and almost non-intrusive (one big function in mtype.c and processing of NamedArgExp at different visitors). .di generation is supported too. |
Copyright © 1999-2021 by the D Language Foundation