D's metaprogramming could be flawed

maik klein via Digitalmars-d digitalmars-d at puremagic.com
Fri Jan 22 04:57:54 PST 2016


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.


More information about the Digitalmars-d mailing list