November 16, 2011
On Wed, 16 Nov 2011 16:47:58 -0500, Simen Kjærås <simen.kjaras@gmail.com> wrote:

> On Wed, 16 Nov 2011 21:00:16 +0100, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>
>> On Wed, 16 Nov 2011 14:26:57 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:
>>
>>> On 11/16/2011 02:22 PM, Steven Schveighoffer wrote:
>>>> On Tue, 15 Nov 2011 13:45:02 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:
>>>>
>>>>> Note that this is an explicit allocation:
>>>>>
>>>>> int[] a = [1,2,3]; // just as explicit as a NewExpression
>>>>>
>>>>> Only the enums "hide" it sometimes, but actually you should know the
>>>>> involved types.
>>>>
>>>> As I've said, there are already ways to explicitly allocate memory. A
>>>> suggested replacement for this is:
>>>>
>>>> int[] a = array(1, 2, 3);
>>>>
>>>> And you could always do:
>>>>
>>>> int[] a = [1, 2, 3].dup;
>>>>
>>>> Nobody complains about having to do:
>>>>
>>>> char[] a = "hello".dup;
>>>>
>>>> I don't see why we couldn't do the same for all array literals.
>>>>
>>>
>>> Because 'immutable' behaves nicely on built-in value types, but not on arbitrary reference types.
>>
>> string is a reference type.  We hear no complaints about strings being stored in ROM and the type of literals being immutable.
>
> immutable(int[][]) a = [[1]];
> int[][] b = a.dup; // Fails.
>
> The problem is with more than a single layer of indirection.

Solved via library:

auto b = a.deepdup;

-Steve
November 16, 2011
On 11/16/2011 10:56 PM, Steven Schveighoffer wrote:
> On Wed, 16 Nov 2011 16:16:48 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:
>
>> On 11/16/2011 09:00 PM, Steven Schveighoffer wrote:
>>> On Wed, 16 Nov 2011 14:26:57 -0500, Timon Gehr <timon.gehr@gmx.ch>
>>> wrote:
>>>
>>>> On 11/16/2011 02:22 PM, Steven Schveighoffer wrote:
>>>>> On Tue, 15 Nov 2011 13:45:02 -0500, Timon Gehr <timon.gehr@gmx.ch>
>>>>> wrote:
>>>>>
>>>>>> Note that this is an explicit allocation:
>>>>>>
>>>>>> int[] a = [1,2,3]; // just as explicit as a NewExpression
>>>>>>
>>>>>> Only the enums "hide" it sometimes, but actually you should know the
>>>>>> involved types.
>>>>>
>>>>> As I've said, there are already ways to explicitly allocate memory. A
>>>>> suggested replacement for this is:
>>>>>
>>>>> int[] a = array(1, 2, 3);
>>>>>
>>>>> And you could always do:
>>>>>
>>>>> int[] a = [1, 2, 3].dup;
>>>>>
>>>>> Nobody complains about having to do:
>>>>>
>>>>> char[] a = "hello".dup;
>>>>>
>>>>> I don't see why we couldn't do the same for all array literals.
>>>>>
>>>>
>>>> Because 'immutable' behaves nicely on built-in value types, but not on
>>>> arbitrary reference types.
>>>
>>> string is a reference type. We hear no complaints about strings being
>>> stored in ROM and the type of literals being immutable.
>>>
>>
>> string is not an immutable type. It is immutable(char)[] and char is a
>> built-in value type.
>>
>> static assert(isMutable!string);
>
> It fits my definition of a valid enum reference type (immutable or
> implicitly castable to immutable). The point is that the data referenced
> is stored in ROM and therefore a) immutable and b) fully defined at
> compile-time.

Indeed. But fact is, the data that is qualified with immutable is not of reference type therefore it behaves nicely. And you don't get that in the general case.

>
>>> Make no mistake, this doesn't cover every current instance array
>>> literals, such as ones which contain necessarily-heap-allocated
>>> entities. Those would be covered by a library function (e.g.
>>> array(...)). But those are not array literals anyways, they are
>>> constructors.
>>>
>>
>> It is true that they are constructors, but they are currently also
>> called array literals.
>
> I'm looking to redefine what a literal means in D.
>
>>>
>>> writeln is not contractually const correct, but that's only because most
>>> of D is not const correct either. For example, Object.toString is not
>>> const, so if you const-ified all writeln args, you couldn't print
>>> objects. But writeln itself does not change any data (a rogue toString
>>> might change data, but that is not common practice).
>>>
>>> Once D becomes const correct, writeln *can* apply const to all it's
>>> parameters.
>>
>> Ok, I see. writeln still could be const-correct in principle. It would
>> need to apply const to the parameters that have a const toString
>> method. But the language is not powerful enough to do that. There is
>> no way to intercept IFTI... I am commonly missing that feature.
>

I am looking for something like this:

template writeln(T...)(T){
    alias writelnImpl!(writelnInferConst!T) writeln;
}

(it is even trivial to parse, unlike normal function definitions!)

> I have an enhancement request in for intercepting IFTI (not sure if it
> applies here): http://d.puremagic.com/issues/show_bug.cgi?id=4998
>

It has a complexity of at least 2^numparams and probably all kinds of odd implications for the compiler internals that would lead to a buggy implementation.

I think this is a better solution:

void foo2(T: ParameterTypeTuple!foo[0])(T t){foo(t);}

Then it is just a matter of applying proper value range propagation for IFTY:

void bar(T: short)(T t){...}

void main(){
    bar(1); // ok
}



>>>
>>>>> If the data is stored in ROM, it should be typed as immutable. If it's
>>>>> not, then it could be modified. The only other option is heap
>>>>> allocation
>>>>> and construction on use, which I've already said is undesirable.
>>>>>
>>>>> If we start from "all array literals and enums that contain references
>>>>> store the referenced data in ROM," then we will find ways to deal with
>>>>> any inconveniences. It's all a matter of what's more important in a
>>>>> system language, performance or convenience.
>>>>
>>>> Both are more important. It is D.
>>>> Everything you need to do is make the enum static immutable instead.
>>>> D is also a scripting language and an application programming language.
>>>>
>>>> auto a = [new Foo, new Bar, new Qux]; // I want that to work.
>>>
>>> auto a = array(new Foo, new Bar, new Qux);
>>>
>>
>> Requiring that works modulo template bloat and breakage of existing
>> code. Also, it does not really buy us anything imho.
>
> I think the bloat is a wash. Every time I use an array literal, there is
> a complete instantiation of what would be in an array template inline in
> the calling function.

With the template function you have that too because the calling function builds the arguments. It is just that you also get the template bloat and an additional function call. Unless it is inlined, of course.

>
> Yes, it breaks code. It's worth it. To avoid a heap allocation for [1,
> 2, 3] is worth breaking code that uses runtime data in an array literal.
> The compiler can be helpful in the error message:
>
> Error somefile.d(345): array literals cannot contain runtime data. Maybe
> you meant:
> array(new Foo, new Bar, new Qux);
>
> I want to point out that currently there is *NO* way to make an
> immutable array literal that lives in ROM. This is unacceptable.
>

There is:

void main(){
    static immutable x = [1,2,3];
}


And the compiler *really* should write this to ROM:

void main(){
    immutable(int)[] x=[1,2,3]; // should be slice to ROM, because the array literal contents are typed as immutable and known during compile time.
}









November 16, 2011
On 11/16/2011 11:39 PM, Timon Gehr wrote:
> I think this is a better solution:
>
> void foo2(T: ParameterTypeTuple!foo[0])(T t){foo(t);}
>
> Then it is just a matter of applying proper value range propagation for
> IFTY:
>
> void bar(T: short)(T t){...}
>
> void main(){
> bar(1); // ok
> }
>

BTW, this already works for your use case:

void foo2(ParameterTypeTuple!foo t){foo(t);}


November 17, 2011
On Wed, 16 Nov 2011 17:39:16 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:

> On 11/16/2011 10:56 PM, Steven Schveighoffer wrote:
>> On Wed, 16 Nov 2011 16:16:48 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:
>>
>>> On 11/16/2011 09:00 PM, Steven Schveighoffer wrote:
>>>> On Wed, 16 Nov 2011 14:26:57 -0500, Timon Gehr <timon.gehr@gmx.ch>
>>>> wrote:
>>>>
>>>>> On 11/16/2011 02:22 PM, Steven Schveighoffer wrote:
>>>>>> On Tue, 15 Nov 2011 13:45:02 -0500, Timon Gehr <timon.gehr@gmx.ch>
>>>>>> wrote:
>>>>>>
>>>>>>> Note that this is an explicit allocation:
>>>>>>>
>>>>>>> int[] a = [1,2,3]; // just as explicit as a NewExpression
>>>>>>>
>>>>>>> Only the enums "hide" it sometimes, but actually you should know the
>>>>>>> involved types.
>>>>>>
>>>>>> As I've said, there are already ways to explicitly allocate memory. A
>>>>>> suggested replacement for this is:
>>>>>>
>>>>>> int[] a = array(1, 2, 3);
>>>>>>
>>>>>> And you could always do:
>>>>>>
>>>>>> int[] a = [1, 2, 3].dup;
>>>>>>
>>>>>> Nobody complains about having to do:
>>>>>>
>>>>>> char[] a = "hello".dup;
>>>>>>
>>>>>> I don't see why we couldn't do the same for all array literals.
>>>>>>
>>>>>
>>>>> Because 'immutable' behaves nicely on built-in value types, but not on
>>>>> arbitrary reference types.
>>>>
>>>> string is a reference type. We hear no complaints about strings being
>>>> stored in ROM and the type of literals being immutable.
>>>>
>>>
>>> string is not an immutable type. It is immutable(char)[] and char is a
>>> built-in value type.
>>>
>>> static assert(isMutable!string);
>>
>> It fits my definition of a valid enum reference type (immutable or
>> implicitly castable to immutable). The point is that the data referenced
>> is stored in ROM and therefore a) immutable and b) fully defined at
>> compile-time.
>
> Indeed. But fact is, the data that is qualified with immutable is not of reference type therefore it behaves nicely. And you don't get that in the general case.

In the general case, there is always a library function for construction.  In other words, what the compiler currently does for array literals can be done in a library.  But a library cannot create ROM space.  The compiler-based features (and CTFE in general) should be helping us create things at compile time, not at run time.  We already have the tools to construct arrays at runtime.

>
> I am looking for something like this:
>
> template writeln(T...)(T){
>      alias writelnImpl!(writelnInferConst!T) writeln;
> }
>
> (it is even trivial to parse, unlike normal function definitions!)

What does writelnInferConst!T do?  I'm afraid I'm not getting what you are saying.

I was thinking writeln should do this:

void writeln(T...)(const T args) {...}

>> I have an enhancement request in for intercepting IFTI (not sure if it
>> applies here): http://d.puremagic.com/issues/show_bug.cgi?id=4998
>>
>
> It has a complexity of at least 2^numparams and probably all kinds of odd implications for the compiler internals that would lead to a buggy implementation.

I'm not a compiler writer, but I don't see how this is the case.

>
> I think this is a better solution:
>
> void foo2(T: ParameterTypeTuple!foo[0])(T t){foo(t);}
>
> Then it is just a matter of applying proper value range propagation for IFTY:
>
> void bar(T: short)(T t){...}
>
> void main(){
>      bar(1); // ok
> }

The issue with all this is, IFTI doesn't work that way:

void foo(T: short)(T t) {}

void main()
{
   foo(1);
}

testifti.d(5): Error: template testifti.foo(T : short) does not match any function template declaration
testifti.d(5): Error: template testifti.foo(T : short) cannot deduce template function from argument types !()(int)

IFTI decides the types of literals before trying to find a proper template to instantiate.  Any possibility to intercept the decision of literal type or of instantiation would be useful.  I think that it's better suited to the constraints, because there is more power there.  But I'm not sure.  If you can find a more straightforward way, I'm all for it.

In any case, I need to update the bug report, because the general case is if foo has an overload.  For instance:

foo(short s);
foo(wstring w);

foo2 should be able to call both with 1 and "hello" without issue.

My driving use case to create the enhancement was creating a wrapper type that intercepted function calls.  I planned to use opDispatch, but it didn't quite work with literals.

>> I think the bloat is a wash. Every time I use an array literal, there is
>> a complete instantiation of what would be in an array template inline in
>> the calling function.
>
> With the template function you have that too because the calling function builds the arguments. It is just that you also get the template bloat and an additional function call. Unless it is inlined, of course.

You are right, I hadn't thought of that.  But what about this:  most array literals are actually literals (made up of CTFE-decided values).  Making them ROM-stored would *eliminate* bloat as compared to the current implementation.

Also, given how template-centric D and phobos are, I think at some point we need to examine how to minimize template bloat in general, by coalescing identical code into one function, or not emitting functions that are always inlined, not to mention avoiding storing templates only used at compile-time in the code (e.g. isInputRange).

>> Yes, it breaks code. It's worth it. To avoid a heap allocation for [1,
>> 2, 3] is worth breaking code that uses runtime data in an array literal.
>> The compiler can be helpful in the error message:
>>
>> Error somefile.d(345): array literals cannot contain runtime data. Maybe
>> you meant:
>> array(new Foo, new Bar, new Qux);
>>
>> I want to point out that currently there is *NO* way to make an
>> immutable array literal that lives in ROM. This is unacceptable.
>>
>
> There is:
>
> void main(){
>      static immutable x = [1,2,3];
> }

Seems rather odd you should have to jump through these hoops to get the compiler to use ROM space.  But I concede that I did not know of this trick.  It does not sway my opinion that CTFE should produce ROM-stored references, and library function should be used for runtime-constructed references.

-Steve
November 17, 2011
On Wed, 16 Nov 2011 18:25:48 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:

> On 11/16/2011 11:39 PM, Timon Gehr wrote:
>> I think this is a better solution:
>>
>> void foo2(T: ParameterTypeTuple!foo[0])(T t){foo(t);}
>>
>> Then it is just a matter of applying proper value range propagation for
>> IFTY:
>>
>> void bar(T: short)(T t){...}
>>
>> void main(){
>> bar(1); // ok
>> }
>>
>
> BTW, this already works for your use case:
>
> void foo2(ParameterTypeTuple!foo t){foo(t);}

My use case is incomplete, I minimized it too much.  I will update it.

-Steve
November 17, 2011
On 11/17/2011 03:19 PM, Steven Schveighoffer wrote:
> On Wed, 16 Nov 2011 17:39:16 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:
>
>> On 11/16/2011 10:56 PM, Steven Schveighoffer wrote:
>>> On Wed, 16 Nov 2011 16:16:48 -0500, Timon Gehr <timon.gehr@gmx.ch>
>>> wrote:
>>>
>>>> On 11/16/2011 09:00 PM, Steven Schveighoffer wrote:
>>>>> On Wed, 16 Nov 2011 14:26:57 -0500, Timon Gehr <timon.gehr@gmx.ch>
>>>>> wrote:
>>>>>
>>>>>> On 11/16/2011 02:22 PM, Steven Schveighoffer wrote:
>>>>>>> On Tue, 15 Nov 2011 13:45:02 -0500, Timon Gehr <timon.gehr@gmx.ch>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> Note that this is an explicit allocation:
>>>>>>>>
>>>>>>>> int[] a = [1,2,3]; // just as explicit as a NewExpression
>>>>>>>>
>>>>>>>> Only the enums "hide" it sometimes, but actually you should know
>>>>>>>> the
>>>>>>>> involved types.
>>>>>>>
>>>>>>> As I've said, there are already ways to explicitly allocate
>>>>>>> memory. A
>>>>>>> suggested replacement for this is:
>>>>>>>
>>>>>>> int[] a = array(1, 2, 3);
>>>>>>>
>>>>>>> And you could always do:
>>>>>>>
>>>>>>> int[] a = [1, 2, 3].dup;
>>>>>>>
>>>>>>> Nobody complains about having to do:
>>>>>>>
>>>>>>> char[] a = "hello".dup;
>>>>>>>
>>>>>>> I don't see why we couldn't do the same for all array literals.
>>>>>>>
>>>>>>
>>>>>> Because 'immutable' behaves nicely on built-in value types, but
>>>>>> not on
>>>>>> arbitrary reference types.
>>>>>
>>>>> string is a reference type. We hear no complaints about strings being
>>>>> stored in ROM and the type of literals being immutable.
>>>>>
>>>>
>>>> string is not an immutable type. It is immutable(char)[] and char is a
>>>> built-in value type.
>>>>
>>>> static assert(isMutable!string);
>>>
>>> It fits my definition of a valid enum reference type (immutable or
>>> implicitly castable to immutable). The point is that the data referenced
>>> is stored in ROM and therefore a) immutable and b) fully defined at
>>> compile-time.
>>
>> Indeed. But fact is, the data that is qualified with immutable is not
>> of reference type therefore it behaves nicely. And you don't get that
>> in the general case.
>
> In the general case, there is always a library function for
> construction. In other words, what the compiler currently does for array
> literals can be done in a library. But a library cannot create ROM
> space. The compiler-based features (and CTFE in general) should be
> helping us create things at compile time, not at run time. We already
> have the tools to construct arrays at runtime.
>
>>
>> I am looking for something like this:
>>
>> template writeln(T...)(T){
>> alias writelnImpl!(writelnInferConst!T) writeln;
>> }
>>
>> (it is even trivial to parse, unlike normal function definitions!)
>
> What does writelnInferConst!T do? I'm afraid I'm not getting what you
> are saying.
>
> I was thinking writeln should do this:
>
> void writeln(T...)(const T args) {...}

As you pointed out, this cannot print types that have a non-const toString method (caching the result could be a perfectly valid reason for that.)

writelnInferConst finds out which parameters can be treated as const and still be printed so that the correct version of writeln may be called.

For example:

class Foo{ // can be printed if const
    string toString()const{return "Foo";}
}

class Bar{ // cannot be printed if const
    string cache;
    string toString(){return cache!is null?cache:(cache="Bar");}
}

template hasConstToString(T){
    enum hasConstToString = is(typeof((const T t){return t.toString();}));
}

template writelnInferConstImpl(T...){
    static if(!T.length) alias T X;
    else static if(hasConstToString!(T[0])){
            alias T[0] _;
            alias TypeTuple!(const(_),writelnInferConst!(T[1..$])) X;
    }else
        alias TypeTuple!(T[0],writelnInferConst!(T[1..$])) X;
}
template writelnInferConst(T...){alias writelnInferConstImpl!T.X writelnInferConst;} // (bug 6966)


static assert(is(writelnInferConst!(Foo,Bar,Foo,Foo,Bar)==TypeTuple!(const(Foo),Bar,const(Foo),const(Foo),Bar)));


The real thing would also do stuff like

actual argument immutable(Foo[])[] => formal argument const(Foo[][]).

In order to get rid of bloat created by pointless instantiations of writelnImpl.



>
>>> I have an enhancement request in for intercepting IFTI (not sure if it
>>> applies here): http://d.puremagic.com/issues/show_bug.cgi?id=4998
>>>
>>
>> It has a complexity of at least 2^numparams and probably all kinds of
>> odd implications for the compiler internals that would lead to a buggy
>> implementation.
>
> I'm not a compiler writer, but I don't see how this is the case.

There were/are quite a few error gagging related bugs. I guess this would be similar.

>
>>
>> I think this is a better solution:
>>
>> void foo2(T: ParameterTypeTuple!foo[0])(T t){foo(t);}
>>
>> Then it is just a matter of applying proper value range propagation
>> for IFTY:
>>
>> void bar(T: short)(T t){...}
>>
>> void main(){
>> bar(1); // ok
>> }
>
> The issue with all this is, IFTI doesn't work that way:
>
> void foo(T: short)(T t) {}
>
> void main()
> {
> foo(1);
> }
>
> testifti.d(5): Error: template testifti.foo(T : short) does not match
> any function template declaration
> testifti.d(5): Error: template testifti.foo(T : short) cannot deduce
> template function from argument types !()(int)
>
> IFTI decides the types of literals before trying to find a proper
> template to instantiate. Any possibility to intercept the decision of
> literal type or of instantiation would be useful. I think that it's
> better suited to the constraints, because there is more power there. But
> I'm not sure. If you can find a more straightforward way, I'm all for it.
>
> In any case, I need to update the bug report, because the general case
> is if foo has an overload. For instance:
>
> foo(short s);
> foo(wstring w);
>
> foo2 should be able to call both with 1 and "hello" without issue.
>
> My driving use case to create the enhancement was creating a wrapper
> type that intercepted function calls. I planned to use opDispatch, but
> it didn't quite work with literals.

Ok, I see the problem. My proposed IFTI template mechanism would save the day.

It would look like this (static foreach would have to be replaced by a recursive mixin template because Walter encountered implementation difficulties).


template OverloadsOf(alias symbol){ // should this be in std.traits?
    alias TypeTuple!(__traits(getOverloads, __traits(parent,symbol), __traits(identifier,symbol))) OverloadsOf;
}

auto wrapper(alias foo)(ParameterTypeTuple!foo args){
    return foo(args);
}
template opDispatch(string op,T...)(T){
    static foreach(foo; OverloadsOf!(mixin(op))){
        alias wrapper!foo opDispatch;
    }
    static if(OverloadsOf!(mixin(op)).length==0) { // we are dealing with a template function
        auto opDispatch(T args){
            return foo(args);
        }
    }
}




>
>>> I think the bloat is a wash. Every time I use an array literal, there is
>>> a complete instantiation of what would be in an array template inline in
>>> the calling function.
>>
>> With the template function you have that too because the calling
>> function builds the arguments. It is just that you also get the
>> template bloat and an additional function call. Unless it is inlined,
>> of course.
>
> You are right, I hadn't thought of that. But what about this: most array
> literals are actually literals (made up of CTFE-decided values). Making
> them ROM-stored would *eliminate* bloat as compared to the current
> implementation.

Yes, and the compiler should do that. That works fine with the current semantics of array literals.

>
> Also, given how template-centric D and phobos are, I think at some point
> we need to examine how to minimize template bloat in general, by
> coalescing identical code into one function, or not emitting functions
> that are always inlined, not to mention avoiding storing templates only
> used at compile-time in the code (e.g. isInputRange).
>

I agree.

>>> Yes, it breaks code. It's worth it. To avoid a heap allocation for [1,
>>> 2, 3] is worth breaking code that uses runtime data in an array literal.
>>> The compiler can be helpful in the error message:
>>>
>>> Error somefile.d(345): array literals cannot contain runtime data. Maybe
>>> you meant:
>>> array(new Foo, new Bar, new Qux);
>>>
>>> I want to point out that currently there is *NO* way to make an
>>> immutable array literal that lives in ROM. This is unacceptable.
>>>
>>
>> There is:
>>
>> void main(){
>> static immutable x = [1,2,3];
>> }
>
> Seems rather odd you should have to jump through these hoops to get the
> compiler to use ROM space. But I concede that I did not know of this
> trick. It does not sway my opinion that CTFE should produce ROM-stored
> references, and library function should be used for runtime-constructed
> references.
>

CTFE should produce ROM-stored data iff it is used during run time, I agree on that. However if the enum is typed as mutable, it should create a copy of the ROM-stored data.









November 17, 2011
On Thu, 17 Nov 2011 12:31:58 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:

> On 11/17/2011 03:19 PM, Steven Schveighoffer wrote:

>>
>> What does writelnInferConst!T do? I'm afraid I'm not getting what you
>> are saying.
>>
>> I was thinking writeln should do this:
>>
>> void writeln(T...)(const T args) {...}
>
> As you pointed out, this cannot print types that have a non-const toString method (caching the result could be a perfectly valid reason for that.)

Caching string representation IMO is not a significant use case.  Not only that, but toString should be deprecated anyways in favor of a stream-based system.

> writelnInferConst finds out which parameters can be treated as const and still be printed so that the correct version of writeln may be called.
>
> For example:
>
> class Foo{ // can be printed if const
>      string toString()const{return "Foo";}
> }
>
> class Bar{ // cannot be printed if const
>      string cache;
>      string toString(){return cache!is null?cache:(cache="Bar");}
> }
>
> template hasConstToString(T){
>      enum hasConstToString = is(typeof((const T t){return t.toString();}));
> }
>
> template writelnInferConstImpl(T...){
>      static if(!T.length) alias T X;
>      else static if(hasConstToString!(T[0])){
>              alias T[0] _;
>              alias TypeTuple!(const(_),writelnInferConst!(T[1..$])) X;
>      }else
>          alias TypeTuple!(T[0],writelnInferConst!(T[1..$])) X;
> }
> template writelnInferConst(T...){alias writelnInferConstImpl!T.X writelnInferConst;} // (bug 6966)
>
>
> static assert(is(writelnInferConst!(Foo,Bar,Foo,Foo,Bar)==TypeTuple!(const(Foo),Bar,const(Foo),const(Foo),Bar)));

If your goal is to reduce template bloat, I think this is not the solution.

But also note that this still does not guarantee const.  I don't really see the point of doing all these templates if you aren't going to guarantee writeln doesn't modify the data.

>>
>> The issue with all this is, IFTI doesn't work that way:
>>
>> void foo(T: short)(T t) {}
>>
>> void main()
>> {
>> foo(1);
>> }
>>
>> testifti.d(5): Error: template testifti.foo(T : short) does not match
>> any function template declaration
>> testifti.d(5): Error: template testifti.foo(T : short) cannot deduce
>> template function from argument types !()(int)
>>
>> IFTI decides the types of literals before trying to find a proper
>> template to instantiate. Any possibility to intercept the decision of
>> literal type or of instantiation would be useful. I think that it's
>> better suited to the constraints, because there is more power there. But
>> I'm not sure. If you can find a more straightforward way, I'm all for it.
>>
>> In any case, I need to update the bug report, because the general case
>> is if foo has an overload. For instance:
>>
>> foo(short s);
>> foo(wstring w);
>>
>> foo2 should be able to call both with 1 and "hello" without issue.
>>
>> My driving use case to create the enhancement was creating a wrapper
>> type that intercepted function calls. I planned to use opDispatch, but
>> it didn't quite work with literals.
>
> Ok, I see the problem. My proposed IFTI template mechanism would save the day.
>
> It would look like this (static foreach would have to be replaced by a recursive mixin template because Walter encountered implementation difficulties).
>
>
> template OverloadsOf(alias symbol){ // should this be in std.traits?
>      alias TypeTuple!(__traits(getOverloads, __traits(parent,symbol), __traits(identifier,symbol))) OverloadsOf;
> }
>
> auto wrapper(alias foo)(ParameterTypeTuple!foo args){
>      return foo(args);
> }
> template opDispatch(string op,T...)(T){
>      static foreach(foo; OverloadsOf!(mixin(op))){
>          alias wrapper!foo opDispatch;
>      }
>      static if(OverloadsOf!(mixin(op)).length==0) { // we are dealing with a template function
>          auto opDispatch(T args){
>              return foo(args);
>          }
>      }
> }

Pardon my saying so, but this looks horrendous.  Not to mention that I don't think it would work.  IFTI instantiates templates, it does not look inside instantiated templates for overloads.

BTW, your proposed IFTI template mechanism, do you have it stated anywhere?  Maybe it fixes the problem I mentioned.

>> Seems rather odd you should have to jump through these hoops to get the
>> compiler to use ROM space. But I concede that I did not know of this
>> trick. It does not sway my opinion that CTFE should produce ROM-stored
>> references, and library function should be used for runtime-constructed
>> references.
>>
>
> CTFE should produce ROM-stored data iff it is used during run time, I agree on that. However if the enum is typed as mutable, it should create a copy of the ROM-stored data.

Well, this is closer than I thought we were to an agreement.  I would agree with this, as long as it could be implicitly cast to immutable or const.

i.e.:

enum foo = [1, 2, 3];

immutable(int)[] a = foo; // no allocation
const(int)[] b = foo; // no allocation

-Steve
November 17, 2011
On 11/17/2011 07:23 PM, Steven Schveighoffer wrote:
> On Thu, 17 Nov 2011 12:31:58 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:
>
>> On 11/17/2011 03:19 PM, Steven Schveighoffer wrote:
>
>>>
>>> What does writelnInferConst!T do? I'm afraid I'm not getting what you
>>> are saying.
>>>
>>> I was thinking writeln should do this:
>>>
>>> void writeln(T...)(const T args) {...}
>>
>> As you pointed out, this cannot print types that have a non-const
>> toString method (caching the result could be a perfectly valid reason
>> for that.)
>
> Caching string representation IMO is not a significant use case. Not
> only that, but toString should be deprecated anyways in favor of a
> stream-based system.
>
>> writelnInferConst finds out which parameters can be treated as const
>> and still be printed so that the correct version of writeln may be
>> called.
>>
>> For example:
>>
>> class Foo{ // can be printed if const
>> string toString()const{return "Foo";}
>> }
>>
>> class Bar{ // cannot be printed if const
>> string cache;
>> string toString(){return cache!is null?cache:(cache="Bar");}
>> }
>>
>> template hasConstToString(T){
>> enum hasConstToString = is(typeof((const T t){return t.toString();}));
>> }
>>
>> template writelnInferConstImpl(T...){
>> static if(!T.length) alias T X;
>> else static if(hasConstToString!(T[0])){
>> alias T[0] _;
>> alias TypeTuple!(const(_),writelnInferConst!(T[1..$])) X;
>> }else
>> alias TypeTuple!(T[0],writelnInferConst!(T[1..$])) X;
>> }
>> template writelnInferConst(T...){alias writelnInferConstImpl!T.X
>> writelnInferConst;} // (bug 6966)
>>
>>
>> static
>> assert(is(writelnInferConst!(Foo,Bar,Foo,Foo,Bar)==TypeTuple!(const(Foo),Bar,const(Foo),const(Foo),Bar)));
>>
>
> If your goal is to reduce template bloat, I think this is not the solution.
>
> But also note that this still does not guarantee const. I don't really
> see the point of doing all these templates if you aren't going to
> guarantee writeln doesn't modify the data.
>
>>>
>>> The issue with all this is, IFTI doesn't work that way:
>>>
>>> void foo(T: short)(T t) {}
>>>
>>> void main()
>>> {
>>> foo(1);
>>> }
>>>
>>> testifti.d(5): Error: template testifti.foo(T : short) does not match
>>> any function template declaration
>>> testifti.d(5): Error: template testifti.foo(T : short) cannot deduce
>>> template function from argument types !()(int)
>>>
>>> IFTI decides the types of literals before trying to find a proper
>>> template to instantiate. Any possibility to intercept the decision of
>>> literal type or of instantiation would be useful. I think that it's
>>> better suited to the constraints, because there is more power there. But
>>> I'm not sure. If you can find a more straightforward way, I'm all for
>>> it.
>>>
>>> In any case, I need to update the bug report, because the general case
>>> is if foo has an overload. For instance:
>>>
>>> foo(short s);
>>> foo(wstring w);
>>>
>>> foo2 should be able to call both with 1 and "hello" without issue.
>>>
>>> My driving use case to create the enhancement was creating a wrapper
>>> type that intercepted function calls. I planned to use opDispatch, but
>>> it didn't quite work with literals.
>>
>> Ok, I see the problem. My proposed IFTI template mechanism would save
>> the day.
>>
>> It would look like this (static foreach would have to be replaced by a
>> recursive mixin template because Walter encountered implementation
>> difficulties).
>>
>>
>> template OverloadsOf(alias symbol){ // should this be in std.traits?
>> alias TypeTuple!(__traits(getOverloads, __traits(parent,symbol),
>> __traits(identifier,symbol))) OverloadsOf;
>> }
>>
>> auto wrapper(alias foo)(ParameterTypeTuple!foo args){
>> return foo(args);
>> }
>> template opDispatch(string op,T...)(T){
>> static foreach(foo; OverloadsOf!(mixin(op))){
>> alias wrapper!foo opDispatch;
>> }
>> static if(OverloadsOf!(mixin(op)).length==0) { // we are dealing with
>> a template function
>> auto opDispatch(T args){
>> return foo(args);
>> }
>> }
>> }
>
> Pardon my saying so, but this looks horrendous. Not to mention that I
> don't think it would work.

Oh, it would certainly work.

> IFTI instantiates templates, it does not look
> inside instantiated templates for overloads.

This works, does this solve the confusion?:

void foo(int){writeln("foo!");}
void bar(double){writeln("bar!");}

template merge(){
    alias foo qux;
    alias bar qux;
}
alias merge!().qux qux;

void main(){
    qux(1);   // calls foo
    qux(1.0); // calls bar
}



>
> BTW, your proposed IFTI template mechanism, do you have it stated
> anywhere? Maybe it fixes the problem I mentioned.

Not yet, I will file a bugzilla enhancement request and post a link here.

What it does is quite simple:

1. Apply normal IFTI instantiation rules to the IFTI template, as if it was a function template.
2. Instantiate the IFTI template with the deduced arguments.
3. The result of the instantiation must be callable with the original arguments. Call it.

This allows the function that is called to have a different (albeit compatible) signature from what IFTI would give you.


>
>>> Seems rather odd you should have to jump through these hoops to get the
>>> compiler to use ROM space. But I concede that I did not know of this
>>> trick. It does not sway my opinion that CTFE should produce ROM-stored
>>> references, and library function should be used for runtime-constructed
>>> references.
>>>
>>
>> CTFE should produce ROM-stored data iff it is used during run time, I
>> agree on that. However if the enum is typed as mutable, it should
>> create a copy of the ROM-stored data.
>
> Well, this is closer than I thought we were to an agreement. I would
> agree with this, as long as it could be implicitly cast to immutable or
> const.
>
> i.e.:
>
> enum foo = [1, 2, 3];
>
> immutable(int)[] a = foo; // no allocation
> const(int)[] b = foo; // no allocation
>
> -Steve

Yes, that is exactly how I would imagine it to work.


1 2 3 4
Next ›   Last »