www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Help needed to learn templates

reply Vinod K Chandran <kcvinu82 gmail.com> writes:
Hi all,
I am trying to learn D templates with Philippe Sigaud's "D 
Templates: A Tutorial". So far so good. I have completed first 19 
pages and in the 20th page, I found an obstacle. This is the code.
```d
module rank1;

template rank(T)
{
     static if (is(T t == U[], U)) // is T an array of U, for some 
type U?
         enum size_t rank = 1 + rank!(U); // then let’s recurse 
down.
     else
     enum size_t rank = 0; // Base case, ending the recursion.
}

module using_rank1;
import rank1;
static assert(rank!(int) == 0);
static assert(rank!(int[]) == 1);
static assert(rank!(int[][]) == 2);
static assert(rank!(int[][][]) == 3);
```
Question 1 - `U` is appearing in the first static if statement. 
But we had to write `U` on the template line, right? Like - 
`template rank(T, U)`
Question 2 - The statif if test is - `T t == U[ ]` What does that 
mean ?
Question 3 - if `T t == U[ ]` is the test, then I think when we 
pass
```d
rank!(int[ ][ ][ ])
```
The test will be `int[ ][ ][ ] == U[ ]`, Right ?
Mar 18 2022
next sibling parent reply Salih Dincer <salihdb hotmail.com> writes:
On Saturday, 19 March 2022 at 05:54:26 UTC, Vinod K Chandran 
wrote:
 Question 1 - `U` is appearing in the first static if statement. 
 But we had to write `U` on the template line, right? Like - 
 `template rank(T, U)`
 Question 2 - The statif if test is - `T t == U[ ]` What does 
 that mean ?
 Question 3 - if `T t == U[ ]` is the test, then I think when we 
 pass
You don't need anything extra while using it... I think U is declare a range. Here is a recursive pattern. Just like the code I wrote below: ```d import std.stdio; alias outer O; struct outer { int i; O * o; } int rank(T)(T* s) { int count = 1; if(s.o is null) return count; return count + rank(s.o); } void main() { auto test = O(1, new O(2, new O(3, new O))); rank(test.o).writeln; test.i.write(", "); test.o.i.write(", "); test.o.o.i.writeln; } /* CONSOLEOUT: 3 1, 2, 3 */ ``` SDB 79
Mar 19 2022
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Saturday, 19 March 2022 at 08:49:02 UTC, Salih Dincer wrote:

Thanks for the reply. You explained the idea very well and it's 
easy to understand for a novice.
Mar 19 2022
prev sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 19 March 2022 at 05:54:26 UTC, Vinod K Chandran 
wrote:

 Question 1 - `U` is appearing in the first static if statement. 
 But we had to write `U` on the template line, right? Like - 
 `template rank(T, U)`
No.
 Question 2 - The statif if test is - `T t == U[ ]` What does 
 that mean ?
The test is not `T t == U[]`. It is `is(T t == U[], U)`. https://dlang.org/spec/expression.html#is-identifier-equal ``` is ( Type Identifier == TypeSpecialization ) The condition is satisfied if Type is semantically correct and is the same as TypeSpecialization. The Identifier is declared to be either an alias of the TypeSpecialization or, if TypeSpecialization is dependent on Identifier, the deduced type. ``` You simply introduce new identifiers. Basically, the test means "is T an array of some type which I would like referred to as U?". Actually, the lower case `t` is not needed there, you can simply write `is(T == U[], U)`.
 Question 3 - if `T t == U[ ]` is the test, then I think when we 
 pass
 ```d
 rank!(int[ ][ ][ ])
 ```
 The test will be `int[ ][ ][ ] == U[ ]`, Right ?
Yes, and `U` then becomes `int[][]`. Which is why the template recurses down and instantiates itself with `U`, until `T` fails the test.
Mar 19 2022
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Saturday, 19 March 2022 at 11:47:53 UTC, Stanislav Blinov 
wrote:
 No.
First of all Thanks for the reply. The answer "No" is a wonder to me. Because, from my point of view, `U` is coming from nowhere. My understanding is, we can use any parameter of a template inside the template. So in this case `U` is not in the parameter list. It is suddenly appearing in that `static if`.
 The test is not `T t == U[]`. It is `is(T t == U[], U)`.
Okay, I understand.
 Actually, the lower case `t` is not needed there, you can 
 simply write `is(T == U[], U)`.
So the `T` is not the type. It's the parameter. Right ? So a template doesn't need a type. Only the parameter, right ? (I think I am too dumb to ask this. Please forgive me.)
 Yes, and `U` then becomes `int[][]`. Which is why the template 
 recurses down and instantiates itself with `U`, until `T` fails 
 the test.
In order to understand this, I need to understand from where the `U` comes.
Mar 19 2022
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 3/19/22 06:38, Vinod K Chandran wrote:
 On Saturday, 19 March 2022 at 11:47:53 UTC, Stanislav Blinov wrote:
 No.
First of all Thanks for the reply. The answer "No" is a wonder to me.
I wrote a chapter about the is expression but it's still mysterious to me. :) ddili.org/ders/d.en/is_expr.html I may be saying some things wrong there but that's my mental model.
 Because, from my point of view, `U` is coming from nowhere.
Agreed. It is similar to U's coming from nowhere below: void foo(U)(U[] array) { } So, in my mental model, that use of the is expression is the same but written in the reverse order from foo above: static if (is(T t == U[], U)) It means "if T matches U[] and U is a type". "a type" because it is just U in the is expression list. I believe at least some of the traits have been added since that doc document was written. I would write it in a much simpler way using template constraints today: template rank(T) { import std.traits : isArray; import std.range : ElementType; static if (isArray!T) { enum size_t rank = 1 + rank!(ElementType!T); } else { enum size_t rank = 0; } } Or one can separate the logic in two template definitions: import std.traits : isArray; template rank(T) if (isArray!T) { import std.range : ElementType; enum size_t rank = 1 + rank!(ElementType!T); } template rank(T) if (!isArray!T) { enum size_t rank = 0; } However, note how the template constraints had to be repeated as isArray!T and !isArray!T in that case.
 My
 understanding is, we can use any parameter of a template inside the
 template. So in this case `U` is not in the parameter list. It is
 suddenly appearing in that `static if`.
In my mental model, the is expression uses at least a part of the template system.
 you can simply write
 `is(T == U[], U)`.
So the `T` is not the type.
T is the type because it is introduced as simply T in the parameter list. If it were 'int T', then it would be an int. So in that sense, it is a type-kind template parameter.
 It's the parameter. Right ? So a template
 doesn't need a type. Only the parameter, right ?
The way I read it is: "T is a type that matches U[] where U is a type as well."
 (I think I am too dumb
 to ask this. Please forgive me.)
Not at all! The is expression is the weirdest part of D. Ali
Mar 19 2022
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 3/19/22 08:58, Ali Çehreli wrote:

 I wrote a chapter about the is expression but it's still mysterious to
 me. :)

    ddili.org/ders/d.en/is_expr.html
Here is the clickable url: http://ddili.org/ders/d.en/is_expr.html I just read it again and I still like what I wrote there. :) (Usually it is the other way around.) Ali
Mar 19 2022
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Saturday, 19 March 2022 at 16:08:33 UTC, Ali Çehreli wrote:
 Here is the clickable url:

   http://ddili.org/ders/d.en/is_expr.html

 I just read it again and I still like what I wrote there. :) 
 (Usually it is the other way around.)

 Ali
Thanks. Let me read that chapter.
Mar 19 2022
prev sibling parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Saturday, 19 March 2022 at 15:58:25 UTC, Ali Çehreli wrote:
 I wrote a chapter about the is expression but it's still 
 mysterious to me. :)

   ddili.org/ders/d.en/is_expr.html
Thanks for the reply. I think I choose the wrong book. I knew about your book but I thought this one is specially written for templates. I will read the template chapters in **`Programming in D`**.
 It means "if T matches U[] and U is a type". "a type" because 
 it is just U in the is expression list.
So as per the eponymous trick, **`enum size_t rank`** will be executed directly. Right ? But in that case, **`rank`** template doesn't take a parameter. There is only the type parameter which is **`T`**. So I am still in confusion about **`U`**.
 I believe at least some of the traits have been added since 
 that doc document was written. I would write it in a much 
 simpler way using template constraints today:

 ```d
 template rank(T) {
   import std.traits : isArray;
   import std.range : ElementType;

   static if (isArray!T) {
     enum size_t rank = 1 + rank!(ElementType!T);

   } else {
     enum size_t rank = 0;
   }
 }
 ```
This template is very easy to understand and I have no confusions about it. Because, it only takes **`T`** as type parameter and there is no magical **`U`**.
 However, note how the template constraints had to be repeated 
 as isArray!T and !isArray!T in that case.
Yeah, I noted.
 Not at all! The is expression is the weirdest part of D.
Oh I see.
Mar 19 2022
prev sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 19 March 2022 at 13:38:42 UTC, Vinod K Chandran 
wrote:
 On Saturday, 19 March 2022 at 11:47:53 UTC, Stanislav Blinov 
 wrote:
 No.
First of all Thanks for the reply. The answer "No" is a wonder to me. Because, from my point of view, `U` is coming from nowhere. My understanding is, we can use any parameter of a template inside the template. So in this case `U` is not in the parameter list. It is suddenly appearing in that `static if`.
It is appearing not in the `static if`, but in the `is` expression, which I described further in the rest of my first reply. Sorry if that wasn't clear.
 The test is not `T t == U[]`. It is `is(T t == U[], U)`.
Okay, I understand.
 Actually, the lower case `t` is not needed there, you can 
 simply write `is(T == U[], U)`.
So the `T` is not the type. It's the parameter. Right ? So a template doesn't need a type. Only the parameter, right ? (I think I am too dumb to ask this. Please forgive me.)
Oh don't worry, this topic is not at all obvious with the `is` expression having its own syntax and semantics. `T` is a type, a type you instantiate `rank` with. `template rank(T)` *does* expect a type as a parameter. The other template syntax - `template foo(alias T)` can take as `T` any symbol, not just a type.
 Yes, and `U` then becomes `int[][]`. Which is why the template 
 recurses down and instantiates itself with `U`, until `T` 
 fails the test.
In order to understand this, I need to understand from where the `U` comes.
It comes from you, the programmer. Like I said before, `is(T == U[], U)` means "is T an array of some type, the type which I (the programmer) would like to refer to as U?". That's all there is to it (well, not quite, but it should suffice for starters). You're simply introducing an identifier. So, when `T` is an `int[][][]`, naturally, `U` becomes an alias to `int[][]` (look at the converse - when `U` is `int[][]`, `U[]` is naturally an `int[][][]`). You can think of that test as this: ```d import std.traits : isDynamicArray; // ... static if (isDynamicArray!T) { alias U = typeof(T.init[0]); // ... } ``` ...which would roughly be the same thing - you test if `T` is a dynamic array of some type, and then make an alias for that array's element type. It's just that the `is` expression allows you to create such alias in situ.
Mar 19 2022
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Saturday, 19 March 2022 at 22:31:19 UTC, Stanislav Blinov 
wrote:
 It is appearing not in the `static if`, but in the `is` 
 expression, which I described further in the rest of my first 
 reply. Sorry if that wasn't clear.
No, it was my mistake, I missed it.
 The other template syntax - `template foo(alias T)` can take as 
 `T` any symbol, not just a type.
I understand this.
 It comes from you, the programmer. Like I said before, `is(T == 
 U[], U)` means "is T an array of some type, the type which I 
 (the programmer) would like to refer to as U?". That's all 
 there is to it (well, not quite, but it should suffice for 
 starters). You're simply introducing an identifier. So, when 
 `T` is an `int[][][]`, naturally, `U` becomes an alias to 
 `int[][]` (look at the converse - when `U` is `int[][]`, `U[]` 
 is naturally an `int[][][]`).
Okay, got it.
 You can think of that test as this:

 ```d
 import std.traits : isDynamicArray;

 // ...

 static if (isDynamicArray!T)
 {
     alias U = typeof(T.init[0]);
     // ...
 }
 ```
Yes, in this case everything is simple and clear.
 ...which would roughly be the same thing - you test if `T` is a 
 dynamic array of some type, and then make an alias for that 
 array's element type. It's just that the `is` expression allows 
 you to create such alias in situ.
Okay. Got the point. Thanks. Now, I understand that why Ali suggest me to learn **`is()`** expression.
Mar 20 2022