March 18, 2012
On Sun, 18 Mar 2012 21:40:10 +1100, FeepingCreature <default_357-line@yahoo.de> wrote:

> On 03/18/12 11:39, FeepingCreature wrote:
>> On 03/18/12 11:36, FeepingCreature wrote:
>>> On 03/18/12 11:29, Derek wrote:
>>>> On Sun, 18 Mar 2012 19:16:02 +1100, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:
>>>>
>>>>> On 3/18/12, Derek <ddparnell@bigpond.com> wrote:
>>>>>> What would be useful is ...
>>>>>>   bar!(a, b, c); // is equivalent to
>>>>>>   bar!(int, int, int).bar(a, b, c);
>>>>>
>>>>> You mean like this?
>>>>>
>>>>> template bar(T...)
>>>>> {
>>>>>     void bar() { writeln(T); }
>>>>> }
>>>>>
>>>>> void main()
>>>>> {
>>>>>     int a = 1, b = 2, c = 3;
>>>>>     bar!(a, b, c);
>>>>> }
>>>>
>>>> Almost, but more like this ...
>>>>
>>>> template add(X,Y,Z)
>>>> {
>>>>    X add(Y a, Z b)
>>>>    {
>>>>        return cast(X) (cast(X)a + cast(X)b);
>>>>    }
>>>> }
>>>>
>>>> void main()
>>>> {
>>>>      double s;
>>>>      int   t;
>>>>      ulong u;
>>>>
>>>>      s = 1.23;
>>>>      t = 123;
>>>>      u = 456;
>>>>
>>>>     t = add!(u,s);
>>>>
>>>>     writefln( "%s %s %s", s,t, u );
>>>> }
>>>>
>>>>
>>>>
>>>> This currently errors with ...
>>>>
>>>>   "Error: template instance add!(u,s) add!(u,s) does not match template declaration add(X,Y,Z)"
>>>>
>>> why would you do that
>>>
>>> what do you want to _do_
>>>
>>> it sounds like you're frantically trying to nail templates into a shape that they really really really aren't meant for
>>>
>>> in any case what is wrong with auto add(T)(T t) { return t[0] + t[1]; }
>>
>> oh
>>
>> you may have misunderstood me
>>
>> a template is a **compile time parameterized namespace**
>>
>> its parameters are **types** and **constants**, not runtime values
>>
>> "add" is a "namespace that is instantiated with the types float and"
>>
>> OOOOOOOOOOOOOOOOOOOOH
>> I get what you want. :D
>>
>> template add(T) {
>>   template add(U...) {
>>     auto add(U u) {
>>       T res;
>>       foreach (value; u) res += value;
>>       return res;
>>     }
>>   }
>> }
>>
>> void main()
>> {
>>      double s;
>>      int   t;
>>      ulong u;
>>
>>      s = 1.23;
>>      t = 123;
>>      u = 456;
>>
>>      t = add!int(u, s);
>>
>>     writefln( "%s %s %s", s, t, u );
>> }
>
> which of course doesn't work because you can't add a double to an int.
>
> So .. maybe I don't get what you want.

The 'adding' is not the point; it could be any functionality. The point I was trying to get across was that it would be useful if the compiler could infer the type parameters of a template instantiation from the types of the data items used in the instantiation reference.

My original code would work if I had of written ...

   t = add!(int, ulong, double)(u, s);

but I was thinking that coding "(int, ulong, double)" is a bit redundant as this information is available to the compiler already as the arguments' types.


And by the way, none of the counter examples so far would compile for me. Still complaining about "add!(u,s) does not match template declaration ..."

-- 
Derek Parnell
Melbourne, Australia
March 18, 2012
On Sun, 18 Mar 2012 21:36:46 +1100, FeepingCreature <default_357-line@yahoo.de> wrote:


> why would you do that

To make coding easier to write AND read.

> what do you want to _do_

Infer template arguments from the data types presented in the data values supplied on the instantiation statement.

> it sounds like you're frantically trying to nail templates into a shape that they really really really aren't meant for

I assumed D templates were a type of template; a model for real runnable code that the compiler can instantiate based on the arguments supplied.

> in any case what is wrong with auto add(T)(T t) { return t[0] + t[1]; }

It doesn't work.

-- 
Derek Parnell
Melbourne, Australia
March 18, 2012
On Sun, 18 Mar 2012 22:00:06 +1100, Derek <ddparnell@bigpond.com> wrote:

>
> The 'adding' is not the point; it could be any functionality. The point I was trying to get across was that it would be useful if the compiler could infer the type parameters of a template instantiation from the types of the data items used in the instantiation reference.

The best I can come up with so far is ...

import std.stdio;
template add(X, Y,Z)
{
     auto add(X c, Y a, Z b)
     {
         return cast(X)a + cast(X)b;
     }
}

void main()
{
     double s;
     int   t;
     ulong u;

     s = 1.23;
     t = 123;
     u = 456;

    writeln(add(t,u,s)); // --> 467

    writeln(add(s,t,s)); // --> 124.23
}


It seems that the templated function's return type is not used when searching for matching templates, so I have to explicitly include something in the function's signature just to use it as the returned data's type.

-- 
Derek Parnell
Melbourne, Australia
March 18, 2012
On 18.03.2012 15:34, Derek wrote:
> On Sun, 18 Mar 2012 22:00:06 +1100, Derek <ddparnell@bigpond.com> wrote:
>
>>
>> The 'adding' is not the point; it could be any functionality. The
>> point I was trying to get across was that it would be useful if the
>> compiler could infer the type parameters of a template instantiation
>> from the types of the data items used in the instantiation reference.
>
> The best I can come up with so far is ...
[snip]

Why not this:

template add(X)
{
     auto add(Y,Z)(Y a, Z b)
     {
         return cast(X)a + cast(X)b;
     }
}
void main()
{
     double s;
     int   t;
     ulong u;

     s = 1.23;
     t = 123;
     u = 456;

    writeln(add!int(u,s)); // --> 467

    writeln(add!double(t,s)); // --> 124.23
}

In short: use type parameters when you parametrize on types. Use IFTI to avoid typing parameters that could be inferred. X type you cast to, clearly can't be inferred from arguments.


>
>
> It seems that the templated function's return type is not used when
> searching for matching templates, so I have to explicitly include
> something in the function's signature just to use it as the returned
> data's type.
>
No just provide type, dummy values smack of flawed dispatch techniques from C++ STL.

-- 
Dmitry Olshansky
March 18, 2012
On 03/18/12 12:05, Derek wrote:
> On Sun, 18 Mar 2012 21:36:46 +1100, FeepingCreature <default_357-line@yahoo.de> wrote:
> 
> 
>> why would you do that
> 
> To make coding easier to write AND read.
> 
>> what do you want to _do_
> 
> Infer template arguments from the data types presented in the data values supplied on the instantiation statement.
> 
>> it sounds like you're frantically trying to nail templates into a shape that they really really really aren't meant for
> 
> I assumed D templates were a type of template; a model for real runnable code that the compiler can instantiate based on the arguments supplied.

Yes but you keep trying to pass runtime arguments to a compiletime construct.

I think you understand what templates are but not how to use them.

> 
>> in any case what is wrong with auto add(T)(T t) { return t[0] + t[1]; }
> 
> It doesn't work.
> 

Okay, let's try this.

template add(T) {
  template add(U...) {
    T add(U u) {
      T res;
      foreach (entry; u) res += cast(T) entry;
      return res;
    }
  }
}

add!int(2, 3, 4.0, 5.0f);
March 19, 2012
On Sun, 18 Mar 2012 11:04:45 +0100
Paulo Pinto <pjmlp@progtools.org> wrote:

> Enterprise software?

Not here, we'll write open-source project. ;)


Sincerely,
Gour


-- 
All living bodies subsist on food grains, which are produced from rains. Rains are produced by performance of yajña [sacrifice], and yajña is born of prescribed duties.

http://atmarama.net | Hlapicina (Croatia) | GPG: 52B5C810


March 19, 2012
On 03/18/12 12:34, Derek wrote:
> On Sun, 18 Mar 2012 22:00:06 +1100, Derek <ddparnell@bigpond.com> wrote:
> 
>>
>> The 'adding' is not the point; it could be any functionality. The point I was trying to get across was that it would be useful if the compiler could infer the type parameters of a template instantiation from the types of the data items used in the instantiation reference.
> 
> The best I can come up with so far is ...
> 
> import std.stdio;
> template add(X, Y,Z)
> {
>      auto add(X c, Y a, Z b)
>      {
>          return cast(X)a + cast(X)b;
>      }
> }
> 
> void main()
> {
>      double s;
>      int   t;
>      ulong u;
> 
>      s = 1.23;
>      t = 123;
>      u = 456;
> 
>     writeln(add(t,u,s)); // --> 467
> 
>     writeln(add(s,t,s)); // --> 124.23
> }
> 
> 
> It seems that the templated function's return type is not used when searching for matching templates, so I have to explicitly include something in the function's signature just to use it as the returned data's type.
> 

You can either let the compiler figure out the right common type or give it explicitly:

auto add(X=void,Y,Z)(Y a, Z b)
{
    static if (is(X==void))
       alias typeof(a+b) RX;
    else
       alias X RX;
    return cast(RX)a + cast(RX)b;
}

Both "add(u,s)" and "add!int(u,s)" will work; note that the first
version returns a 'double', but that's probably what you want, when
not explicitly asking for an 'int'.

artur
March 19, 2012
On Mar 17, 2012, at 10:14 AM, Entity325 wrote:

> (Sorry if this is the wrong place for this, or if there's already a thread in existence which would be better.  If either of these is the case, simply point me in the right direction, and I'll be on my way.)
> 
> My first interaction with Templates was about 5 years ago, in a C++ class at my university.  I immediately thought "A general type?  This would be great for my logger!" and tried to implement them into the small library I had been developing to save time on the class assignments.
> 
> Naturally, I didn't understand them, so after a couple of half-hearted attempts, I gave up and went back to doing things the way I had before.  I've avoided Templates since then, because they don't make any sense!

I see this a lot, and it's why I wrote the chapter on templates for Learn to Tango with D.  I don't think I can sort out releasing the chapter though.  One approach is to think of templates as compile-time polymorphism.  In Java, you might create a linked-list as:

class List {
    void insert(Object e) {
        head = new Node(e, head);
    }

    static class Node {
        Object elem;
        Node next;

        this(Object e, Node n) {
            elem = e;
            next = n;
        }
    }
}


This works fine so long as everything you store in the list derives from Object, and you have to remember what the list holds so when you examine an element you cast it to Integer or whatever.  But both of these are problems.  First, you can't store concrete types (int, float, etc) in the list, and second, the compiler can't help you make sure you only store instances of Integer or whatever in the list (for the record, Java does have auto-boxing and generics now to deal with these issues).  In D, we can address these problems by converting List to a template class.

Templates are really just a means of telling the compiler to generate code according to some simple rules.  So looking at the List class above, if we want it to hold anything instead of just Objects, we replace instances of "Object" above with a type signifier.  The long form is easiest to understand:

template (T) {
    class List {
        void insert(T e) {
            head = new Node(e, head);
        }

        static class Node {
            T elem;
            Node next;

            this(T e, Node n) {
                elem = e;
                next = n;
            }
        }
    }
}


Notice that all we did to create this template was replace "Object" with an arbitrarily chosen name "T", then enclose the whole thing in a "template" block with one parameter, T.  This tells the compiler that what's contained is a template with a variable type T.  Let's say you declare one instance of this container:

auto list = new List!(int);


The compiler sees "int" as the parameter for template argument "T" and generates code for the list above with "int" written wherever "T" is, so:

class List {
    void insert(int e) {
        head = new Node(e, head);
    }

    static class Node {
        int elem;
        Node next;

        this(int e, Node n) {
            elem = e;
            next = n;
        }
    }
}


This is done early enough during compilation that it's as if you wrote the code by hand, so you'll get a compile error if you try to insert something that isn't an int, etc.

The declaration of template types that introduce their own scope (ie. structs and classes) can eliminate the enclosing "template" scope and move the template list down to after the type name, so the original template declaration above is equivalent to:

class List (T) {
    ...
}


Template functions work exactly the same way, except that the compiler can infer the types of template parameters based on the function argument list, so:

template (T) {
    T add(T a, T b) {
        return a + b;
    }


which can be written in a more compact form as:

T add(T)(T a, T b) {
    return a + b;
}


Note how the template list moved after the function name, just like with the List class above.  Now, you can call this as:

auto result = add!(int)(1, 2);
assert(result == 3);


Or let the compiler determine T for you.  The arguments above are both of type int, so if you write:

auto result = add(1, 2);
assert(result == 3);


The compiler sees that the arguments for add() are type int and does the replacement automatically.

Now fast-forward from the original conception of templates to today.  D also allows you to pass data as template arguments, so this also works:

template (int x) {
    int add(int y) {
        return x + y;
    }
}


or again:

int add(int x)(int y) {
    return x + y;
}


And calling:

int val = 7;
auto result = add!(5)(val);


Generates the function:


int add!(5)(int y) {
    return 5 + y;
}


The more advanced template stuff is just riffs on these ideas.  It all works by having the compiler generate code, replacing type and variable symbols in the template list with whatever was specified when the template was called.  And back to the original motivation above, this gives you the performance and safety of hand-written code with the ease of maintenance of polymorphic code, since you only have to maintain a single copy.

I dunno if any of that helped, but if not, maybe some more directed questions in D.learn?  The basic idea behind templates is very straightforward.  The biggest problem seems to be that most people talk about them like they're some sort of black magic, so finding a straightforward explanation can be surprisingly hard.
March 19, 2012
On 3/19/12 1:59 PM, Sean Kelly wrote:
> I see this a lot, and it's why I wrote the chapter on templates for
> Learn to Tango with D.  I don't think I can sort out releasing the
> chapter though.  One approach is to think of templates as
> compile-time polymorphism.  In Java, you might create a linked-list
> as:
[snip]

There have been a couple of long good answers in this thread, and it would be a shame to let them scroll by the way newsgroup messages do. I suggest authors to repackage their posts as blog entries or articles.

Andrei
March 19, 2012
On Mon, Mar 19, 2012 at 02:27:04PM -0500, Andrei Alexandrescu wrote:
> On 3/19/12 1:59 PM, Sean Kelly wrote:
> >I see this a lot, and it's why I wrote the chapter on templates for Learn to Tango with D.  I don't think I can sort out releasing the chapter though.  One approach is to think of templates as compile-time polymorphism.  In Java, you might create a linked-list as:
> [snip]
> 
> There have been a couple of long good answers in this thread, and it would be a shame to let them scroll by the way newsgroup messages do. I suggest authors to repackage their posts as blog entries or articles.
[...]

It would be nice if somebody could write up a general introduction to the idea of templates (not specific to D), the motivations behind it, perhaps some historical background, and then eventually lead up to advanced template tricks using D as the prime example.

Then not only we can point newbies at the document, but it can also serve as D propaganda.

I'd take a stab at this, except that my free time is limited and most of it is currently being used for refining my AA implementation so that it can (hopefully) replace the current AA mess.


T

-- 
"Hi." "'Lo."