www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Flexible Default Function Parameters via structs with Nullable Fields

reply Mike Parker <aldacron gmail.com> writes:
Victor Porton shows how he uses string mixins to generate structs 
with Nullable fields at compile time to help him pass arbitrary 
subsets of explicit and default arguments to functions in D.

The blog:
https://dlang.org/blog/2019/04/29/flexible-default-function-parameters/

Reddit:
https://www.reddit.com/r/programming/comments/bip83x/flexible_default_function_parameters_in_d/
Apr 29 2019
parent reply JN <666total wp.pl> writes:
On Monday, 29 April 2019 at 13:08:59 UTC, Mike Parker wrote:
 Victor Porton shows how he uses string mixins to generate 
 structs with Nullable fields at compile time to help him pass 
 arbitrary subsets of explicit and default arguments to 
 functions in D.

 The blog:
 https://dlang.org/blog/2019/04/29/flexible-default-function-parameters/

 Reddit:
 https://www.reddit.com/r/programming/comments/bip83x/flexible_default_function_parameters_in_d/
Yeah... this isn't a topic that will get any attention on Reddit. It might be nifty by D standards, but for a person not familiar with D first thought will be "why not just use named/keyword arguments", second thought will be "oh, D doesn't have them? so why not just use builder pattern then?".
Apr 30 2019
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 30 April 2019 at 08:20:29 UTC, JN wrote:
 It might be nifty by D standards, but for a person not familiar 
 with D
Or, as someone familiar with D, I wonder why not just use a plain struct. D allows you to set initial values for struct members plainly. To be frank, I haven't been impressed with any of this author's posts.
Apr 30 2019
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 30 April 2019 at 13:10:54 UTC, Adam D. Ruppe wrote:
 On Tuesday, 30 April 2019 at 08:20:29 UTC, JN wrote:
 It might be nifty by D standards, but for a person not 
 familiar with D
Or, as someone familiar with D, I wonder why not just use a plain struct. D allows you to set initial values for struct members plainly.
Yeah, though I can see some use cases for a struct with all nullable fields and a way to combine with a regular version of that struct. This could be made a lot easier than in the article: import std.traits : FieldNameTuple; import std.typecons : Nullable; struct Partial(T) if (is(T == struct)) { static foreach (e; FieldNameTuple!T) mixin("Nullable!(typeof(__traits(getMember, T, e))) "~e~";"); } auto combine(T, PT)(T t, PT pt) if (is(PT == Partial!T)) { T result; static foreach (e; FieldNameTuple!T) __traits(getMember, result, e) = __traits(getMember, pt, e).get(__traits(getMember, t, e)); return result; } struct S { int x,y; } unittest { S a = S(1,2); Partial!S b; b.x = 3; assert(a.combine(b) == S(3,2)); } Now, for the abomination that is callMemberFunctionWithParamsStruct!(t, "f")(combined)... It's just t.f(combined.tupleof) in a bad disguise, and I really can't see the benefit. Lastly, the use of a mixin to define the struct ensures you can't put methods on the struct, thus drastically reducing usability. All in all, it's a fun beginner's project, but the quality may not be good enough that it should be on the blog. -- Simen
Apr 30 2019
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 30 April 2019 at 13:44:00 UTC, Simen Kjærås wrote:
 Now, for the abomination that is 
 callMemberFunctionWithParamsStruct!(t, "f")(combined)... It's 
 just t.f(combined.tupleof) in a bad disguise, and I really 
 can't see the benefit.
If you are doing function parameters, there are two kinda fun things you can do. (Personally, I kinda prefer to just do hand-written builder patters, nicer to document, often easier to read, but this is D, so let's go nuts!) First, this is an automatically generated struct with members corresponding to function parameters: --- void foo(int a, string cool = "low temperature", int[] c = [1, 2, 3]) { import std.stdio; writeln("a = ", a); writeln("cool = ", cool); writeln("c = ", c); } // this works for free functions, but not delegates, function pointers, or other callable objects // it also will not automatically call a method, but you can build parameters for it. struct ParamsFor(F...) if(F.length == 1) { static if(is(typeof(F[0]) Parameters == __parameters)) { static foreach(idx, _; Parameters) { static if(__traits(compiles, ((Parameters[idx .. idx + 1] i) => i[0])())) mixin(" Parameters[idx .. idx + 1][0] // type "~__traits(identifier, Parameters[idx .. idx + 1])~" // name = ((Parameters[idx .. idx + 1] i) => i[0])() // initial value ;"); else mixin(" Parameters[idx .. idx + 1][0] // type "~__traits(identifier, Parameters[idx .. idx + 1])~" // name // no initial value ;"); } } else static assert(0, typeof(F[0]).stringof ~ " is not a plain callable"); auto opCall()() { static if(__traits(compiles, F[0](this.tupleof))) return F[0](this.tupleof); else static assert(0, __traits(identifier, F[0]) ~ " is not callable this way since it needs a `this` object, do it yourself on the outside with obj.method(params.tupleof)"); } } class Test { void foo(int a, int b = 10, int c = 20) { import std.stdio; writeln(a, " ", b, " ", c); } } void main() { ParamsFor!foo params; params.c = [4,5,6]; params(); // calls foo(params.tupleof) for you ParamsFor!(Test.foo) p2; p2.c = 30; auto f = new Test(); //p2(); // will static assert cuz of this f.foo(p2.tupleof); // use this instead } --- But there, required parameters can be left out too - you don't have to set anything. (It also doesn't work with const params and other such troubles, but that really complicates this idea - and is part of why I prefer a hand-written builder thing, so you can handle all those details explicitly.) We can solve that with a constructor. Right below the first static if in the example, add: --- static if(!__traits(compiles, ((Parameters _) {}) () )) { disable this(); this(Parameters params) { this.tupleof = params; } } --- And now you get an obscure error if you don't specific parameters when creating the Params object. But.... eh I don't love it. Regardless, still though, this stuff is kinda cool. And if you combine with the `with` statement: --- void main() { // this assumes the version with the constructor // but if you didn't add that code, just remove // the 5 and thus ParamsFor!foo() with(ParamsFor!foo(5)) { c = [4,5,6]; // set the param c... opCall(); // call the function } } --- so yeah, kinda cool.
Apr 30 2019