Imperative templates

Timon Gehr timon.gehr at gmx.ch
Sat Jul 16 17:36:06 PDT 2011


On Saturday 16 July 2011 16:58:25 Robert Clipsham wrote:
> So in several presentations given about D that I've seen there's a list
> of supported paradigms given which looks something like this:
>
>   - Imperative
>   - Metal
>   - Object-oriented
>   - RAII
>   - Functional
>   - Generic
>   - Generative
>   - Concurrent
>
> (That list from
> http://assets.en.oreilly.com/1/event/45/The%20D%20Programming%20Language%20P
> resentation.pdf ). But if this is the case, why do we not get this choice
> for template programming?
>
> Currently templates have to be written in a functional manner, which is
> incredibly difficult if you're used to imperative programming, and I've
> found the code that it leads to is really difficult to maintain, even if
> it's well commented (this may not be the case for folk that are well
> versed in functional programming).
>
> What I'd like to suggest is something like this:
> ----
> /// Template to get every other type in a type tuple
> template AlternateEven(T...)
> {
>      enum U = TypeTuple!();
>      for (size_t i = 0; i < T.length; i++)
>      {
>          if ((i & 1) == 0)
>          {
>              U ~= T[i];
>          }
>      }
>      alias U AlternateEven;
> }
> ----
>
> To do this currently you need something like:
> ----
> template AlternateEven(T...)
> {
>      alias AlternateEvenImpl!(0, T) AlternateEven;
> }
>
> template AlternateEvenImpl(size_t i, T...) if ((i & 1) == 0)
> {
>      static if (i < T.length)
>      {
>          alias TypeTuple!(T[i], AlternateEvenImpl!(i + 1, T))
> AlternateEvenImpl;
>      }
>      else
>      {
>          alias TypeTuple!() AlternateEvenImpl;
>      }
> }
>
> template AlternateEvenImpl(size_t i, T...) if ((i & 1) != 0)
> {
>      static if (i < T.length)
>      {
>          alias AlternateEvenImpl!(i + 1, T) AlternateEvenImpl;
>      }
>      else
>      {
>          alias TypeTuple!() AlternateEvenImpl;
>      }
> }
> ----
>
> Which is a lot more code, and a lot harder to understand at a quick
> glance. It gets even worse if you want to do something more
> complicated[1][2].
>
> I don't think my example is the best example for a possible syntax, but
> that could be worked on. What does anyone else think?
>
> [1] https://github.com/mrmonday/dmd/blob/js/dsrc/bind/util.d#L134
> [2]
> https://github.com/mrmonday/serenity/blob/master/serenity/persister/Sqlite.d
> #L126

You can actually compute most things in an imperative way using this idiom:

enum x = {/*imperative code to compute value of x*/}();

When TypeTuples are involved, you can get away with mostly imperative code and
careful workarounds for bugs and limitations of the compiler:


import std.stdio, std.conv;
import std.array, std.range;
import std.algorithm;


import std.typecons, std.typetuple;
// Appender should be made ctfe-able soon...
string[] ctfeSplit(string s,char d)/*insert sensible constraint here*/{
	string[] r;
	size_t l=0;
	foreach(i,c;s) if(c==d) r ~= s[l..i], l=i+1;
	if(l<s.length) r~=s[l..$];
	return r;
}
string[] stripParens(string[] s){ // modifying in-place segfaults the compiler... bug.
	// strips the parens around types stringof inserts, that make the result invalid
D code... enhancement?
	string[] r;
	foreach(x;s) if(x[0]=='(') r~=x[1..$-1]; else r~=x;
	return r;
}
template TypeTupleToStringArray(T...){
	enum TypeTupleToStringArray={
		string s = T.stringof[6..$-1];
		return stripParens(ctfeSplit(s,','));
	}();
}
alias TypeTupleToStringArray TTTSA;

template SATTImpl(string s){ // only piece of somewhat functional code: needed to
construct the TypeTuple
	enum a = mixin(s);
	pragma(msg,a);
	static if(a.length == 0) alias TypeTuple!() val;
	else static if(a[0][0]=='"') alias
TypeTuple!(mixin(a[0]),SATTImpl!(a[1..$].stringof).val) val; // does not work with
types... enhancement.
	else mixin("alias TypeTuple!("~a[0]~",SATTImpl!(a[1..$].stringof).val) val;");
}
template StringArrayToTypeTuple(string s){ //have to compensate for restrictions
on template parameter types...enhancement.
	alias SATTImpl!s.val StringArrayToTypeTuple;
}
alias StringArrayToTypeTuple SATTT;

// finally, an imperative implementation of AlternateEven!
template AlternateEvenImpl(T...){
	enum U ={
		string[] t=TTTSA!T, u;
		for (size_t i = 0; i < T.length; i++){
			if(!(i & 1)) u ~= t[i];
		}
		return u;
	}();
	alias SATTT!(U.stringof) val;
}
template AlternateEven(T...){
	alias AlternateEvenImpl!T.val AlternateEven;
}

void main(){
	assert(AlternateEven!(1,2,int,double,2,"hello","hi\"hi\"").stringof ==
TypeTuple!(1,int,2,"hi\"hi\"").stringof); // have to use stringof... bug/enhancement.
}

But usually, well written functional code is both more concise and better
maintainable than imperative code.

Cheers,
-Timon


More information about the Digitalmars-d mailing list