www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D's metaprogramming could be flawed

reply maik klein <maikklein googlemail.com> writes:
This will be a small rant that might be completely unjustified 
and it is not meant to talk ill about D.

I am new to D and I run into a road blocking issue 
https://issues.dlang.org/show_bug.cgi?id=10541

So I set myself to fixing it. Asking around the irc, it seems 
that the biggest problem was that tuples always copy and we need 
something that can hold references.

I was going to create a TupleRef but I run into some issues.

At first I created something similar to an std::reference_wrapper 
which I am also not sure was needed in the first place but here 
it is.


struct RefWrapper(T){
   T* value;

   this(ref T v){
     value = &v;
   }
   ref T get(){
     return *value;
   }
}
auto refWrapper(T)(ref T t){
   return RefWrapper!(T)(t);
}

RefWrapper is probably flawed, but let's pretend it works.


The general idea was to have a tuple of type 
Tuple!(RefWrapper(T1), RefWrapper(T2), ... , RefWrapper(TN));

It would probably work that way, but D has its own reference 
semantics with `ref`. I thought it would be nicer if you can 
reuse `ref` notation. For example:


foreach(t; something){
   t.get();
}

//vs

foreach(ref t; something){
}

That meant I needed to create a wrapper that would automatically 
unbox every RefWrapper.

struct TupleRef(Ts...){
   import std.meta;
   alias RefTs = staticMap!(RefWrapper, Ts);
   Tuple!RefTs refTs;
   this(ref Ts ts){
     refTs = ????
   }
}

Now we are at the problem that I constantly run into since I 
started with D.

It seems that D doesn't have a way to express C++'s fold 
expression/template expansions.


For example in C++ I could write

std::make_tuple(std::ref(ts)...);

and it would expand like

std::make_tuple(std::ref(t1), std::ref(t2), ... , std::ref(tn));

I would love to use staticMap! but it seems to only work for 
types, it could be altered slightly so that it would also work 
with normal functions but the values would still have to be read 
at compile time.


For this case I used my little helper function:

auto mapToTuple(alias f,T...)(){
   import std.typecons;
   static if(T.length == 0)  {
     return tuple();
   }
   else{
     return tuple(f(T[0]), mapToTuple!(f, T[1..$]).expand);
   }
}


TupleRef then became

struct TupleRef(Ts...){
   import std.meta;
   alias RefTs = staticMap!(RefWrapper, Ts);
   Tuple!RefTs refTs;
   this(Ts ts){
     refTs = mapToTuple!(refWrapper, ts);
   }
}


In this case everything seems to work nicely but the problem 
still exists.

For example I need to be able to expand TupleRef like this

f(someTupleRef.expand);
f(t[0].get(), t[1].get(), ... , t[n].get());
//f takes the arguments by ref

This time I can not use a tuple because it would copy the value. 
I also can not use `AliasSeq` because it needs it values at 
compile time.



Another small problem for TupleRef is opIndex. It seems that the 
[index] notation with opIndex doesn't allow me to express the use 
of compile time values.

It seems that tuple somehow manages this with

struct Tuple{
   ...
   alias expand this;
   ...
}

But I don't think I can use the same technique here.

I thought I would write a bigger post that explains my thoughts 
instead of asking in the irc. I hope it didn't sound too harsh 
which was not my intent.
Jan 22
next sibling parent reply rsw0x <anonymous anonymous.com> writes:
On Friday, 22 January 2016 at 12:57:54 UTC, maik klein wrote:
 ...
You're looking for AliasSeq in std.meta, it's a tup—er, finite ordered list of types :)
Jan 22
parent reply maik klein <maikklein googlemail.com> writes:
On Friday, 22 January 2016 at 13:21:11 UTC, rsw0x wrote:
 On Friday, 22 January 2016 at 12:57:54 UTC, maik klein wrote:
 ...
You're looking for AliasSeq in std.meta, it's a tup—er, finite ordered list of types :)
I am already aware of AliasSeq as I have written above. But I could have misused it, would you mind showing an example with TupleRef?
Jan 22
parent reply rsw0x <anonymous anonymous.com> writes:
On Friday, 22 January 2016 at 13:28:00 UTC, maik klein wrote:
 On Friday, 22 January 2016 at 13:21:11 UTC, rsw0x wrote:
 On Friday, 22 January 2016 at 12:57:54 UTC, maik klein wrote:
 ...
You're looking for AliasSeq in std.meta, it's a tup—er, finite ordered list of types :)
I am already aware of AliasSeq as I have written above. But I could have misused it, would you mind showing an example with TupleRef?
Sorry, I must have skipped that. Is there a reason you're explicitly using tuples? Unless I'm misunderstanding you, you're looking for something like... struct Baz(V...) { V vals; } which can be used like... void foo(int, int, int) { } void main(){ auto i = Baz!(int, int, int)(); foo(i.vals); } or am I way off base? If so, is this similar to what you're looking for? http://dpaste.dzfl.pl/cbae4c4ed7af Sorry if I'm nowhere near what you meant.
Jan 22
parent reply maik klein <maikklein googlemail.com> writes:
On Friday, 22 January 2016 at 14:19:30 UTC, rsw0x wrote:
 On Friday, 22 January 2016 at 13:28:00 UTC, maik klein wrote:
 On Friday, 22 January 2016 at 13:21:11 UTC, rsw0x wrote:
 On Friday, 22 January 2016 at 12:57:54 UTC, maik klein wrote:
 ...
You're looking for AliasSeq in std.meta, it's a tup—er, finite ordered list of types :)
I am already aware of AliasSeq as I have written above. But I could have misused it, would you mind showing an example with TupleRef?
Sorry, I must have skipped that. Is there a reason you're explicitly using tuples? Unless I'm misunderstanding you, you're looking for something like... struct Baz(V...) { V vals; } which can be used like... void foo(int, int, int) { } void main(){ auto i = Baz!(int, int, int)(); foo(i.vals); } or am I way off base? If so, is this similar to what you're looking for? http://dpaste.dzfl.pl/cbae4c4ed7af Sorry if I'm nowhere near what you meant.
I think that should work but it only works because you do an implicit conversion with get which is quite nice. But I was also looking for a more general solution. I think the mixin solution could be quite nice. static template unpack(alias f){ pragma(inline) auto into(alias target, Args...)(ref Args args){ import std.conv; enum s = `target(`~iota(Args.length).map!(i=>text(`f(args[`,i,`])`)).join(",")~`)`; return mixin(s); } } and use it like: auto r = unpack!(i => i * 2).into!((a, b) => a + b)(1,2); The only semi weird thing is that I can use this directly with my version of TupleRef like this: void foo(ref int, ref int){ } unpack!((r)=> r.get()).into!(foo)(tref.refTs.expand); I think that is because "lambda/delegates" can not express ref return types? So I think I need to do this: ref auto get(R)(R r){ return r.get(); } unpack!(get).into!(foo)(tref.refTs.expand);
Jan 23
parent maik klein <maikklein googlemail.com> writes:
On Saturday, 23 January 2016 at 12:13:16 UTC, maik klein wrote:
 On Friday, 22 January 2016 at 14:19:30 UTC, rsw0x wrote:
 [...]
I think that should work but it only works because you do an implicit conversion with get which is quite nice. [...]
I forgot to show what `tref` was in my last comment. int i = 1; int i2 = 2 auto tref = TupleRef!(int,int)(i, i2);
Jan 23
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 01/22/2016 01:57 PM, maik klein wrote:
 It seems that D doesn't have a way to express C++'s fold
 expression/template expansions.


 For example in C++ I could write

 std::make_tuple(std::ref(ts)...);

 and it would expand like

 std::make_tuple(std::ref(t1), std::ref(t2), ... , std::ref(tn));
 ...
 For example I need to be able to expand TupleRef like this

 f(someTupleRef.expand);
 f(t[0].get(), t[1].get(), ... , t[n].get());
Custom expansions can be built with string mixins. mixin(`f(`~iota(t.length).map!(i=>text(`t[`,i,`].get()`)).join(",")~`);`);
Jan 22