Thread overview
Applying a tuple to a function (and more)
Sep 18, 2010
Juanjo Alvarez
Sep 18, 2010
Philippe Sigaud
Sep 19, 2010
Juanjo Alvarez
Sep 19, 2010
Philippe Sigaud
Sep 19, 2010
Juanjo Alvarez
Sep 19, 2010
Philippe Sigaud
Sep 19, 2010
Juanjo Alvarez
Sep 20, 2010
bearophile
Sep 20, 2010
Philippe Sigaud
Sep 20, 2010
bearophile
September 18, 2010
Hi,

I've just arrived to D 2.0 and after reading Andrei's book I'm loving everything I'm seeing (except the bugs, of course).

I wanted to ask how these would be done, because I can't find how to do it:

1. Having the strings, defined at compile time, "MyClass" and "mymethod", how could I could I get to a delegate to MyClass.mymethod?

2. Is there any way to apply a tuple to a function, expanding as arguments?

Like:

void f(int a, double b) {}
auto tup = tuple(42, 3.14);
f(<magic goes here with tup expanding as 42, 3.14>);

Thanks,
Juanjo

September 18, 2010
On Sat, Sep 18, 2010 at 19:59, Juanjo Alvarez <juanjux@gmail.com> wrote:

> I wanted to ask how these would be done, because I can't find how to do it:
>
> 1. Having the strings, defined at compile time, "MyClass" and "mymethod", how could I could I get to a delegate to MyClass.mymethod?
>

You can insert them in a piece of code as a string, and then mix it in:

enum string code = "auto deleg = &" ~ className ~ "." ~ methodName ~ ";";
// auto deleg = &MyClass.mymethod;
mixin(code); // created deleg, you're good to go.



>
> 2. Is there any way to apply a tuple to a function, expanding as arguments?
>
> Like:
>
> void f(int a, double b) {}
> auto tup = tuple(42, 3.14);
> f(<magic goes here with tup expanding as 42, 3.14>);
>


Use the .field or .expand alias, exposed by Tuple!() to get a direct access to the internal expression tuple.

f(tup.expand); // cracks tup open

Recently, bug 2800 was corrected and you can also directly access a tuple's fields by indexing:

double d = tup[1];  // or simply:     auto d = tup[1];

Before that, you had to do

double d = tup.field[1];

Recently, tuples also got a .length member. That's handy.
As for expanding tuples inside functions if you do that regularly, I suggest
you use a function adaptor, like this:

template tuplify(alias fun) if (isCallable!fun)
{
    ReturnType!fun tuplify(Tuple!(ParameterTypeTuple!fun) tup)
    {
        return fun(tup.expand);
    }
}

Usage:

alias tuplify!f tf;
tf(tup);

tf is a function, like any other. It accepts a Tuple!(int,double), cracks it
open and pass it to f.

I don't know if you need some explanation on how it works?
Here it is anyway: tuplify is a template, a parameterized piece of code.
tuplify takes only one template parameter, by alias. This means you give it
a name, any identifier in the current scope. You do not give it a type.
That's what allows you to write tuplify!f.

Here the result of the template instantation is a function with the same name 'tuplify'. When a template exposes only one identifier with its own name, it's as if the code inside the template got instantiated right there. So tuplify!f is a function. It could have been a function template, or any other D construct. I used "alias tuplify!f tf;" to be able to use tf as ... an alias (doh!) to tuplify!f. But I could also have used:

auto tf = &(tuplify!f);  // tuplify!f is a function, tf is a pointer to this
function.

You can treat tf as any other D function : pass it around, give it as a argument to other templates or functions, etc.

The template constraint if(isCallable!fun) means tuplify can only be instantiated on callables (function, delegates, pointer to functions, structs or classes with opCall defined, etc). Note that tuplify works on _all_ callables! After all, the only property I use in the code is that f can be called with ().

isCallable lives here: http://digitalmars.com/d/2.0/phobos/std_traits.html#isCallable

I use the ReturnType and ParameterTypeTuple templates to do a little compile-time introspection

You can find ReturnType and ParameterTypeTuple here: http://digitalmars.com/d/2.0/phobos/std_traits.html#ReturnType

So the internally created function accepts a Tuple!(the types fun needs) as argument. I indicate to the compiler it will return what fun returns, but I could have used auto there instead.

Philippe


September 19, 2010
Philippe Sigaud wrote:


>> 1. Having the strings, defined at compile time, "MyClass" and "mymethod", how could I could I get to a delegate to MyClass.mymethod?
>>
> 
> You can insert them in a piece of code as a string, and then mix it in:
> 
> enum string code = "auto deleg = &" ~ className ~ "." ~ methodName ~ ";";
> // auto deleg = &MyClass.mymethod;
> mixin(code); // created deleg, you're good to go.

I tried it and worked like a charm (but I've changed my code so instead of two strings to class + method, a fully qualified non member function address is used so I have a little more flexibility).


>> 2. Is there any way to apply a tuple to a function, expanding as arguments?

> Use the .field or .expand alias, exposed by Tuple!() to get a direct access to the internal expression tuple.
> 
> f(tup.expand); // cracks tup open

Now, that is exactly what I wanted. It even works if the function has aditional arguments before the expanded tuple one's:

//
void f(int a, int b, double c) {
   writeln(a); writeln(b); writeln(c);
}

auto tup = tuple(42, 3.14);
f(1, tup.expand);
//

Wonderful. I wish it was better documented on http://digitalmars.com/d/2.0/phobos/std_typecons.html


> As for expanding tuples inside functions if you do that regularly, I suggest you use a function adaptor, like this:
> 
> template tuplify(alias fun) if (isCallable!fun)
> {
>     ReturnType!fun tuplify(Tuple!(ParameterTypeTuple!fun) tup)
>     {
>         return fun(tup.expand);
>     }
> }

Nice to know. I got you excellent explanation.

Thanks * 100,

Juanjo

September 19, 2010
On Sun, Sep 19, 2010 at 03:43, Juanjo Alvarez <juanjux@gmail.com> wrote:

> > enum string code = "auto deleg = &" ~ className ~ "." ~ methodName ~ ";";
> > // auto deleg = &MyClass.mymethod;
> > mixin(code); // created deleg, you're good to go.
>
> I tried it and worked like a charm (but I've changed my code so instead of
> two strings to class + method, a fully qualified non member function
> address
> is used so I have a little more flexibility).
>

String mixins are quite powerful, if a bit clunky at times.

See also __traits(getMember, ...)
http://digitalmars.com/d/2.0/traits.html   (look for getMember)



> > Use the .field or .expand alias, exposed by Tuple!() to get a direct access to the internal expression tuple.
> >
> > f(tup.expand); // cracks tup open
>
> Now, that is exactly what I wanted. It even works if the function has aditional arguments before the expanded tuple one's:
>
> //
> void f(int a, int b, double c) {
>   writeln(a); writeln(b); writeln(c);
> }
>


Yes, because tuple.expand is directly the expression tuple stored inside
Tuple!(...)
When you have a typetuple in a template, like this:

template List(T...)
{
}

The T... part is a typetuple: an array of types, if you wish. You can get
its length, index it, slice it, iterate on it, etc.
You can have a variable with this strange 'type' and also get its length,
etc.

template List(T...)
{
    T t;
}

t is an expression tuple. std.typecons.Tuple is nothing more than the
preceding List!(...), with a few more functionalities.
So, something interesting is that when you do tup.expand, you obtain a bunch
of values of different types, all perfectly individually typed and usable.
You can get one by indexing, slice it, etc.

So, given your new f, you can even do:

auto tup = tuple(42, 3.14);
f(tup.expand[0], 1, tup.expand[1]);

Though in this particular case, it's cleaner to do:

f(tup[0], 1, tup[1]);


Wonderful. I wish it was better documented on
> http://digitalmars.com/d/2.0/phobos/std_typecons.html
>
>
Yes, typecons.Tuple has lot of nifty things going for it, but there are not
all documented.
You can open a bug/enhancement request at http://d.puremagic.com/issues/



> Nice to know. I got you excellent explanation.
>
> Thanks * 100,
>
>
My pleasure,

Philippe


September 19, 2010
Philippe Sigaud wrote:

> String mixins are quite powerful, if a bit clunky at times.

Both true, for what I'm seeing of them.

> See also __traits(getMember, ...)
> http://digitalmars.com/d/2.0/traits.html   (look for getMember)

Nice, with getMembers I could redesign again my test code for using objects instead of a virtual function, but I'll keep the current design because is simpler for me (I'm still adapting my brain to all this genericity).

On my current implementation with functions I still have a doubt:

I'm using template-functions like this one for my callbacks:

void somefunc(T...) (string regex, T args...) {
    foreach (arg; args) {
        writeln(arg);
    }
}

Then, I write the "selector" for it (in other part of the program) like this:

auto t = tuple("someregex", &(module.somefunc!(int, double)),tuple(2,3.14));

And then in a third module, I get those selectors and bind them to the functions like:

auto selectable = t.field[1];
selectable(t.field[0], t.field[2].expand); // Called!

With your help, this works like a charm.

But sometimes there will be no variable arguments to "somefunc" (only the first string), and in that case I don't know what would be the correct syntax to specify that the variable number of arguments will have no args, both for the template instantiation and for the tuple:

auto t = tuple("bla", &(module.somefunc!(void), tuple(void)); // Fails
auto t = tuple("bla", &(module.somefunc!(void, Tuple!(void)()); // Fails too



> Yes, typecons.Tuple has lot of nifty things going for it, but there are
> not all documented.
> You can open a bug/enhancement request at http://d.puremagic.com/issues/

I'll do it. I guess currently most of you here check the source before the
documentation since the former looks to be pretty outdated sometimes.


September 19, 2010
> instead of a virtual function, but I'll keep the current design because is simpler for me (I'm still adapting my brain to all this genericity).
>

What languages are you used to? You seem to do quite well with genericity :)


>
> On my current implementation with functions I still have a doubt:
>
> I'm using template-functions like this one for my callbacks:
>
> void somefunc(T...) (string regex, T args...) {
>    foreach (arg; args) {
>        writeln(arg);
>    }
> }
>

OK, no need for the last "..."  args is already variadic. What you wrote is
equivalent  "I want any number of lists of any number of types".
Just use:

void somefunc(T...)(string regex, T args) {
...



>
> Then, I write the "selector" for it (in other part of the program) like
> this:
>
> auto t = tuple("someregex", &(module.somefunc!(int,
> double)),tuple(2,3.14));
>

A tuple in a tuple, nice.


>
> And then in a third module, I get those selectors and bind them to the functions like:
>
> auto selectable = t.field[1];
> selectable(t.field[0], t.field[2].expand); // Called!
>
> With your help, this works like a charm.
>
> But sometimes there will be no variable arguments to "somefunc" (only the first string), and in that case I don't know what would be the correct syntax to specify that the variable number of arguments will have no args, both for the template instantiation and for the tuple:
>
> auto t = tuple("bla", &(module.somefunc!(void), tuple(void)); // Fails
> auto t = tuple("bla", &(module.somefunc!(void, Tuple!(void)()); // Fails
> too
>

Use tuple(). Or Tuple!()()

Another possibility could be to write your selector like this:

auto t = tuple("bla", &(someFun), 2, 3.14);

That is, the values are not wrapped in a tuple, but directly stored in t.
That way, if you want no value, just do:
auto t = tuple("bla", &(someFun)); // there is an expression tuple of length
0 there.

auto selectable = t[1];
selectable(t[0], t.expand[2..$]);

for now, you cannot directly get a slice from the tuple. You either use
expand (an expression tuple, so slicable) or use the Tuple.slice member
function (never used it).


As to the difference between Tuple!(void) and Tuple!():
void is a 'valid' type (sort of). It's just a type that has no value. So
Tuple!(void) and Tuple!() are different.
You can define the type Tuple!(int, void, int), but you cannot create a
value of that type, because the void field cannot have a value.

Tuple!() is a tuple with no field, an empty tuple. You can create it all
right. It's just, its length is zero, and it has no field.
If you open it with expand, you get a zero-length typetuple. It's like an
array of length zero: it's valid, but you cannot access its internal values,
because there is none. Other programming languages call this the Unit type,
because it's a (the) type that has only _one_ value, namely Tuple!()().


If you ask for the parameter typetuple for a no-arg function, you'll get a zero-length typetuple:

int foo() { return 0;}
alias ParameterTypeTuple!foo Pfoo;
assert(is(Pfoo == TypeTuple!());

Pfoo pfoo; pfoo is a tuple of length 0.
auto a = foo(pfoo); // we can pass it to a function with no arg.


Philippe


September 19, 2010
Philippe Sigaud wrote:

> What languages are you used to? You seem to do quite well with genericity :)

What I've done profesionally and personally in the last year would be 90%
Python, 5% Java and 5% C++. So yes, since Python is like a D with an
uberauto and everything being runtime templates the real problem I have is
with generic declarations :)


> OK, no need for the last "..."  args is already variadic. What you wrote
> is
> equivalent  "I want any number of lists of any number of types".
> Just use:
> 
> void somefunc(T...)(string regex, T args) {

Changued!

>> auto t = tuple("someregex", &(module.somefunc!(int,
>> double)),tuple(2,3.14));
>>
> 
> A tuple in a tuple, nice.

And maybe a tuple in a tuple in a tuple :) (see my other question later)

> auto t = tuple("bla", &(someFun), 2, 3.14);
> 
> That is, the values are not wrapped in a tuple, but directly stored in t.
> That way, if you want no value, just do:
> auto t = tuple("bla", &(someFun)); // there is an expression tuple of
> length 0 there.
> 
> auto selectable = t[1];
> selectable(t[0], t.expand[2..$]);

This is much better, no doubt. I've changued that part too (I'm really
learning a lot with your posts, you should think about writing a book about
D too).

Now I've two new roadblocks:

Roadblock1:

If the tuple is defined in the same file as the code expanding it (with the template function in another file), it works. But if I move the tuple definition to another file (urls.d) the compiler gives me the error:

Error: Unions with overlapping fields are not yet supported in CTFE
urls.d(9): Error: cannot evaluate tuple("^/home/$",& index,42,3.14) at
compile time
urls.d(9): Error: cannot evaluate tuple("^/home/$",& index,42,3.14) at
compile time

That is, this works:

// file: bindselector.d
import std.typecons;
import views;

void main() {
     // tuple defined here
     auto selector_data = tuple( "^/home/$", &(views.index!(int, double)),
42, 3.14 );
     auto sel_regex_var  = selector_data.field[0];
     auto sel_view_var = selector_data.field[1];
     sel_view_var(sel_regex_var, selector_data.expand[2..$]);
}


But moving the declaration of "selector_data" to a file urls.d and then importing that file from bindselector.d gives me that error... bug?


Roadblock2: The next step is to define multiple tuples in urls.d inside some iterable data structure, so bindselector.d can import that and do its thing inside a foreach.

The problem here is related again with my limitations declaring generics. Since every tuple will have a different type signature, I tought that the logical structure to group them would be... yes, another tuple :) Then I would foreach on that structure and bind the params, etc.

First try (Fail-1):

// ----------------------------------------
auto selector_data_tuples = tuple(
                                   tuple( "^/home/$", &(views.index!(int,
double)), 42, 3.14 )
                                 );

foreach(selector_data; selector_data_tuples) {
     auto sel_regex_var  = selector_data.field[0];
     auto sel_view_var = selector_data.field[1];
     sel_view_var(sel_regex_var, selector_data.expand[2..$]);
}
// ----------------------------------------

Compile error: Error: cannot infer type for selector_data

Logical, since the type of the iterated element in the foreach is fixed, and I'm iterating over tuples of different types.

Then I did (Fail-2):

// -----------------------------------------------------------
     for (int i = 0; i < selector_data_tuples.length; i++) {
         auto selector_data = selector_data_tuples.field[i];
         auto sel_regex_var  = selector_data.field[0];
         auto sel_view_var = selector_data.field[1];
         sel_view_var(sel_regex_var, selector_data.expand[2..$]);
     }
// -----------------------------------------------------------

Which fails with a "Error: Integer constant expression expected instead of cast(uint)i".

Is there any other way to iterate over a tuple with values of different tuples? Does d provide any other data structures better suited for this case? (other than an array of Object and a gazillion ugly casts).



September 20, 2010
Juanjo Alvarez:

> But if I move the tuple definition to another file (urls.d) the compiler gives me the error:

It seems a compiler bug. If not already present it probably needs to be added.


> Is there any other way to iterate over a tuple with values of different tuples?

You may use a typetuple, but those have to be used with lot of care, because the foreach on them is static, so I don't think that's what you want. D is statically typed, so you can't use it a dynamic typed language, as you are trying to do now.


> Does d provide any other data structures better suited for this case? (other than an array of Object and a gazillion ugly casts).

You may need something like an array of variants. Or to redesign your solution.

Bye,
bearophile
September 20, 2010
On Sun, Sep 19, 2010 at 18:33, Juanjo Alvarez <juanjux@gmail.com> wrote:

> Philippe Sigaud wrote:
>
> > What languages are you used to? You seem to do quite well with genericity :)
>
> What I've done profesionally and personally in the last year would be 90% Python, 5% Java and 5% C++. So yes, since Python is like a D with an uberauto and everything being runtime templates the real problem I have is with generic declarations :)
>

OK, that's why you're using tuples for everything :)
Python's tuples are cool. I think I *learnt* what tuples were while dabbling
in Python a few years ago. (Haskell's ones are great too).

Note that the 'real' D tuples are expression tuples (instantiated
typetuples). The "T..." thingies in templates, once instantiated with  T t;
They know their length, are indexable, slicable, iterable, etc. In a way
they are random-access range with a length, only with heterogeneous types.
But I don't think they were conceived as tuples in other languages, not at
first. I'd guess that, for Walter, they were just a natural
extension/cleaning for templates, based on C++. But they are like Python's
tuples! Except for one blatant limitation: they cannot be returned from a
function :(
But you can return a templated struct. So the basic idea is to wrap them in
Tuple(T...) to be able to return them. Then std.typecons.tuple does its best
to offer a interesting access to the underlying expression tuple. But it's
not a first-class citizen in D: you cannot iterate them nor slice them...
For now, because the situation has improved.

What many people coming from C++/Java do is putting their stuff in structs/classes. They tend not to use tuples that much.



This is much better, no doubt. I've changued that part too (I'm really
> learning a lot with your posts, you should think about writing a book about D too).
>

What I should do is moving my a** and have some code reviewed for Phobos. But it seems I can't find the time. When I've half an hour, I try to help here instead :)




> Now I've two new roadblocks:
>
> Roadblock1:
>
> If the tuple is defined in the same file as the code expanding it (with the template function in another file), it works. But if I move the tuple definition to another file (urls.d) the compiler gives me the error:
>
> Error: Unions with overlapping fields are not yet supported in CTFE
> urls.d(9): Error: cannot evaluate tuple("^/home/$",& index,42,3.14) at
> compile time
> urls.d(9): Error: cannot evaluate tuple("^/home/$",& index,42,3.14) at
> compile time
>
> That is, this works:
>
> // file: bindselector.d
> import std.typecons;
> import views;
>
> void main() {
>     // tuple defined here
>     auto selector_data = tuple( "^/home/$", &(views.index!(int, double)),
> 42, 3.14 );
>     auto sel_regex_var  = selector_data.field[0];
>     auto sel_view_var = selector_data.field[1];
>     sel_view_var(sel_regex_var, selector_data.expand[2..$]);
> }
>
>
> But moving the declaration of "selector_data" to a file urls.d and then importing that file from bindselector.d gives me that error... bug?
>

What do you mean, you moved the declaration of selector_data ? You declared it outside main() in another module? I don't know why, I never declare date outside functions/objects.

Maybe if you declare it in the 'root scope' of the module (I don't know how to call that: the basic scope of a module, the one where the module name; is), it's initialized at compile time. And at compile time, I'm not sure things like addresses &(...) have a sense.

Did you try to make selector_data a function?

auto selector_data() { return tuple("^/home/$", &(views.index!(int,
double)),42, 3.14 );}



> Roadblock2:
> The next step is to define multiple tuples in urls.d inside some iterable
> data structure, so bindselector.d can import that and do its thing inside a
> foreach.
>
> The problem here is related again with my limitations declaring generics. Since every tuple will have a different type signature, I tought that the logical structure to group them would be... yes, another tuple :) Then I would foreach on that structure and bind the params, etc.
>
> First try (Fail-1):
>
> // ----------------------------------------
> auto selector_data_tuples = tuple(
>                                   tuple( "^/home/$", &(views.index!(int,
> double)), 42, 3.14 )
>                                 );
>
> foreach(selector_data; selector_data_tuples) {
>     auto sel_regex_var  = selector_data.field[0];
>     auto sel_view_var = selector_data.field[1];
>     sel_view_var(sel_regex_var, selector_data.expand[2..$]);
> }
> // ----------------------------------------
>
> Compile error: Error: cannot infer type for selector_data
>
> Logical, since the type of the iterated element in the foreach is fixed,
> and
> I'm iterating over tuples of different types.
>

Oh, but you can iterate on an expression tuple alright, even when all its
elements have different types. I don't know why bearophile doesn't like
that.
It's just, as I've said higher up in this message, that tuple()/Tuple!() is
a struct, not a 'real' tuple: it offers a nice access to the wrapped
expression tuple, but not as much as you'd like. Use .expand to get direct
access:


    auto t = tuple(1, 3.14, "abc");
    foreach(i, field; t.expand) // get index and value
    {
        writefln("field #%s is of type %s and has value
%s.",i,typeof(field).stringof, to!string(field));
    }

If you want to do some type sorcery, you can also iterate on the types. You can get access to them by the .Types alias in Tuple!().

foreach(i,Type;  t.Types) // will iterate on (int,double,string) and not on
(1, 3.14, "abc")
{...}


Philippe


September 20, 2010
Philippe Sigaud:
> Oh, but you can iterate on an expression tuple alright, even when all its elements have different types. I don't know why bearophile doesn't like that.

I like the iteration on typetuples (the nomenclature here is a mess, see bug 4113), but you have to use such iteration with care because it's a static foreach, your code may become very long (this is why in bug 4085 I have asked to require a "static foreach" to iterate on typetuples, to help the programmer see the difference better).

Bye,
bearophile