www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Understanding alias template parameters

reply JG <someone somewhere.com> writes:
Hi,

Could someone possibly help me to understand why the commented 
line doesn't compile?


```d
import std;

struct MapResult(R,F)
{
     R r;
     const F f;
     auto empty() { return r.empty; }
     auto front() { return f(r.front); }
     void popFront() { r.popFront; }
     auto save() { return typeof(this)(r.save,f); }
}

auto myMap(alias f, R)(R r) {
     return MapResult!(R,typeof(f))(r,f);
}


void main()
{
    int function(int) f = x=>2*x;
    iota(10).myMap!f.writeln;
    //iota(10).myMap!(x=>2*x).writeln; <--- Why doesn't this 
compile?

}
```

(I do know that Phobos's map works differently with better 
performance).
Apr 21 2022
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 4/21/22 14:02, JG wrote:

 Could someone possibly help me to understand why the commented line
 doesn't compile?
iota(10).myMap!(x=>2*x).writeln; It is because x=>2*x is just a template. I don't know why the compiler chooses 'void' for typeof(f) but apparently that's how it represents the types of function templates. One way of changing the code is to use the following but it does not really fit because of course you may want types other than 'int': (int x) => 2 * x // Now the type is known The more logical thing to do is to stay with alias template parameters for MapResult as well. I am not sure why you need the member 'f' so I commented it out but now it compiles after some trivial changes: import std; struct MapResult(R, alias f) // <== HERE { R r; // const F f; auto empty() { return r.empty; } auto front() { return f(r.front); } void popFront() { r.popFront; } auto save() { return typeof(this)(r.save); } } auto myMap(alias f, R)(R r) { pragma(msg, typeof(f)); return MapResult!(R, f)(r); } void main() { int function(int) f = x=>2*x; iota(10).myMap!f.writeln; iota(10).myMap!(x=>2*x).writeln; } Ali
Apr 21 2022
parent Salih Dincer <salihdb hotmail.com> writes:
On Thursday, 21 April 2022 at 21:38:14 UTC, Ali Çehreli wrote:
 ```d
 auto myMap(alias f, R)(R r) {
   pragma(msg, typeof(f));
     return MapResult!(R, f)(r);
 }
 ```
It looks delicious when the convenience function works magic with Voldemort: ```d import std.range, std.stdio; auto myMap(alias f, R)(R r) { struct Map { auto empty() { return r.empty; } auto front() { return f(r.front); } void popFront() { r.popFront; } } return Map(); } void main() { // with convenience function: alias func = (int x) => 2 * x; auto range = 1.iota(11); range.myMap!func.writeln; range.myMap!(x => 2 * x).writeln; // with only struct: struct MapResult(alias range, alias f) { auto empty() { return range.empty; } auto front() { return f(range.front); } void popFront() { range.popFront; } } MapResult!(range, func) mp; foreach(result; mp) result.writeln; } /* OUTPUT: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] 2 4 6 8 10 12 14 16 18 20 */ ``` SDB 79
Apr 21 2022
prev sibling next sibling parent Paul Backus <snarwin gmail.com> writes:
On Thursday, 21 April 2022 at 21:02:47 UTC, JG wrote:
 Hi,

 Could someone possibly help me to understand why the commented 
 line doesn't compile?


 ```d
 import std;

 struct MapResult(R,F)
 {
     R r;
     const F f;
     auto empty() { return r.empty; }
     auto front() { return f(r.front); }
     void popFront() { r.popFront; }
     auto save() { return typeof(this)(r.save,f); }
 }

 auto myMap(alias f, R)(R r) {
     return MapResult!(R,typeof(f))(r,f);
 }


 void main()
 {
    int function(int) f = x=>2*x;
    iota(10).myMap!f.writeln;
    //iota(10).myMap!(x=>2*x).writeln; <--- Why doesn't this 
 compile?

 }
 ```
When you write a lambda without type annotations, like `x => 2*x`, the compiler interprets it as a function template. So in this case, it's the same as if you'd written ```d T func(T)(T x) { return 2*x; } iota(10).myMap!func.writeln; ``` The problem is, you can't store a template in a variable: ```d auto f = func; // doesn't work - func does not have a value ``` So, when you try to create a `MapResult` with the template `x => 2*x` as one of its member variables, it doesn't work. By the way, you may have noticed that the error message you got says something about variables of type `void` not being allowed. That's because of [a long-standing bug][1] that causes `typeof` returns `void` when applied to a template. However, that bug is not the cause of the error here--even if it were fixed, your code still would not work. [1]: https://issues.dlang.org/show_bug.cgi?id=7947
Apr 21 2022
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Apr 21, 2022 at 09:02:47PM +0000, JG via Digitalmars-d-learn wrote:
 Hi,
 
 Could someone possibly help me to understand why the commented line
 doesn't compile?
 
 
 ```d
 import std;
 
 struct MapResult(R,F)
 {
     R r;
     const F f;
     auto empty() { return r.empty; }
     auto front() { return f(r.front); }
     void popFront() { r.popFront; }
     auto save() { return typeof(this)(r.save,f); }
 }
 
 auto myMap(alias f, R)(R r) {
     return MapResult!(R,typeof(f))(r,f);
 }
 
 
 void main()
 {
    int function(int) f = x=>2*x;
    iota(10).myMap!f.writeln;
    //iota(10).myMap!(x=>2*x).writeln; <--- Why doesn't this compile?
 
 }
 ```
Check the compiler's error message carefully: /tmp/test.d(6): Error: variable `test.MapResult!(Result, void).MapResult.f` variables cannot be of type `void` ^^^ The error comes from line 6, which is:
    const F f;
A lambda literal like `x=>2*x` is a template, so you cannot store it in a variable. You need to either explicitly wrap it in a delegate, or forward the alias to your struct as a template parameter, e.g.: struct MapResult(alias f, R) { ... // remove the line `const F f;` } auto myMap(alias f, R)(R r) { return MapResult!(f, R)(r); } T -- Truth, Sir, is a cow which will give [skeptics] no more milk, and so they are gone to milk the bull. -- Sam. Johnson
Apr 21 2022
prev sibling parent reply JG <someone somewhere.com> writes:
On Thursday, 21 April 2022 at 21:02:47 UTC, JG wrote:
 Hi,

 Could someone possibly help me to understand why the commented 
 line doesn't compile?


 ```d
 import std;

 struct MapResult(R,F)
 {
     R r;
     const F f;
     auto empty() { return r.empty; }
     auto front() { return f(r.front); }
     void popFront() { r.popFront; }
     auto save() { return typeof(this)(r.save,f); }
 }

 auto myMap(alias f, R)(R r) {
     return MapResult!(R,typeof(f))(r,f);
 }


 void main()
 {
    int function(int) f = x=>2*x;
    iota(10).myMap!f.writeln;
    //iota(10).myMap!(x=>2*x).writeln; <--- Why doesn't this 
 compile?

 }
 ```

 (I do know that Phobos's map works differently with better 
 performance).
Thank you to all for the great replies. To fix it one could do: ```d import std; struct MapResult(R,F) { R r; const F f; auto empty() { return r.empty; } auto front() { return f(r.front); } void popFront() { r.popFront; } auto save() { return typeof(this)(r.save,f); } } auto myMap(alias f, R)(R r) { static if(__traits(compiles,f!(typeof(R.init.front)))) { auto fun = f!(typeof(R.init.front)); return MapResult!(R,typeof(fun))(r,fun); } else { return MapResult!(R,typeof(f))(r,f); } } void main() { int function(int) f = x=>2*x; iota(10).myMap!f.writeln; iota(10).myMap!(x=>2*x).writeln; } ``` In response to the change to "alias", which has several upsides including faster code. I would note it also has some downsides including every lambda produces a new type so that (at the moment) the following assert holds: ```d auto r = iota(10).map!(x=>x+1); auto s = iota(10).map!(x=>x+1); assert(!is(typeof(r)==typeof(s))); ```
Apr 22 2022
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 4/22/22 01:04, JG wrote:

 In response to the change to "alias", which has several upsides
 including faster code. I would note it also has some downsides including
 every lambda produces a new type so that (at the moment) the following
 assert
 holds:
I got confused a little bit there. To make sure: alias would cause different template instantiation. That's why the following types are different for 'map'.
 ```d
 auto r = iota(10).map!(x=>x+1);
 auto s = iota(10).map!(x=>x+1);
 assert(!is(typeof(r)==typeof(s)));
 ```
When you use your MyMap, the range types are the same. Having an alias parameter, myMap() function will still cause multiple instances but that's life. Ali
Apr 22 2022
prev sibling parent Salih Dincer <salihdb hotmail.com> writes:
On Friday, 22 April 2022 at 08:04:16 UTC, JG wrote:
 On Thursday, 21 April 2022 at 21:02:47 UTC, JG wrote:
 Hi,

 Could someone possibly help me to understand why the commented 
 line doesn't compile?
Good job, works great! Potential is high: ```d void main() { alias type = real; auto range = iota!type(1, 4, .5); alias fun1 = (type a) => a * a; alias fun2 = (type a) => a * 2; alias fun3 = (type a) => a + 1; alias fun4 = (type a) => a - 1; alias fun5 = (type a) => a / 2; alias func = type function(type a); alias MR = MapResult!(typeof(range), func); MR[] mr; tuple(fun1, fun2, fun3, fun4, fun5) .each!(f => mr ~= MR(range, f)); mr.writefln!"%-(%s\n%)"; } /* OUTPUT: [1, 2.25, 4, 6.25, 9, 12.25] [2, 3, 4, 5, 6, 7] [2, 2.5, 3, 3.5, 4, 4.5] [0, 0.5, 1, 1.5, 2, 2.5] [0.5, 0.75, 1, 1.25, 1.5, 1.75] */ ```
Apr 22 2022