July 06, 2013
On Saturday, 6 July 2013 at 12:51:18 UTC, TommiT wrote:
> The function template foo accepts int, const(int) and immutable(int) as the type parameter, because they're all convertible to inout(int).

I didn't mean _convertible_ to inout, but rather that inout _accepts_ const, immutable or nothing.
July 06, 2013
On Saturday, 6 July 2013 at 12:51:18 UTC, TommiT wrote:
> On Saturday, 6 July 2013 at 11:35:28 UTC, Manu wrote:
>> The way that makes the most sense to me is:
>>
>> void f(T)(Unqual!T t) {}
>
> But my syntax makes pretty much sense too:
>
> void foo(inout T)(T var)
> {
>     var = 42;
> }
>
> void main()
> {
>     foo!(int)(1);
>     foo!(const int)(2);
>     foo!(immutable int)(3);
> }

Some more details of this proposed feature:

inout(T) foo(inout T)(T a, T b)
{
    return var;
}

void bar(inout T)(T a, T b)
{
    a = b;
}

void main()
{
    const int con;
    immutable int imm;

    foo(con, con); // OK (returns const int)
    foo(con, imm); // Error: returned inout(T) is ambiguous

    bar(con, con); // OK
    bar(con, imm); // OK (no ambiguity because inout not used)
}
July 06, 2013
On Saturday, 6 July 2013 at 13:30:02 UTC, TommiT wrote:
> Some more details of this proposed feature:
>
> inout(T) foo(inout T)(T a, T b)
> {
>     return var;
> }
>
> void bar(inout T)(T a, T b)
> {
>     a = b;
> }
>
> void main()
> {
>     const int con;
>     immutable int imm;
>
>     foo(con, con); // OK (returns const int)
>     foo(con, imm); // Error: returned inout(T) is ambiguous
>
>     bar(con, con); // OK
>     bar(con, imm); // OK (no ambiguity because inout not used)
> }

Another approach, if we wanted a different syntax, would be to add a new keyword 'mutable'. Like immutable overrides const, mutable would override both const and immutable:

static assert(is(mutable(const(int)) == int));
static assert(is(mutable(immutable(int)) == int));

Here's how it would look:

T foo(T)(mutable(T) a, mutable(T) b)
{
    a = b = 123;
    return a;
}

void main()
{
    const int con;
    immutable int imm;

    foo(con, con); // OK (returns const int)
    foo(con, imm); // Error: T is ambiguous
}
July 06, 2013
On 6 July 2013 22:27, Namespace <rswhite4@googlemail.com> wrote:

> The way that makes the most sense to me is:
>>
>> void f(T)(Unqual!T t) {}
>>
>> Given an immutable(T) for instance that I want to call with, it would
>> instantiate the template with the signature void f(T), and then attempt to
>> call it with the immutable(T). If immutable(T) is not convertible to the
>> argument T, then it would produce a compile error as if attempting to call
>> any such function that just receives T.
>>
>> The point here is that I want more control over the signature of the instantiated template (reduce the number of permutations generated for various calls). Template blow-out is perhaps the biggest and most well known day-to-day problem in C++, and tools like this may be very valuable to mitigate the disaster.
>>
>
> It seems that your code works if you put the Template Type explicit:
> ----
> import std.stdio;
> import std.traits : Unqual;
>
> void foo(T)(Unqual!T a) {
>         writeln(typeof(a).stringof, " <-> ", T.stringof);
> }
>
> void main() {
>         int a;
>         const int b;
>         immutable int c;
>
>         //foo(c); /// Error
>         foo!(typeof(a))(a);
>         foo!(typeof(b))(b);
>         foo!(typeof(c))(c);
> }
> ----
>

Indeed, hence my point that the type deduction is the key issue here. It should be possible... maybe a bit tricky though.


July 06, 2013
I feel like it would be much better to do this with type deduction, than to
introduce new keywords and concepts...
Saying type deduction is hard is probably not accurate. It's either
possible, or impossible. If it's mechanically possible, then it's easy for
the compiler to do. If it's impossible (or ambiguous), then an error would
be thrown in those cases. I'm sure the compiler is capable of working out
if it's impossible and complaining.


On 6 July 2013 23:50, TommiT <tommitissari@hotmail.com> wrote:

> On Saturday, 6 July 2013 at 13:30:02 UTC, TommiT wrote:
>
>> Some more details of this proposed feature:
>>
>> inout(T) foo(inout T)(T a, T b)
>> {
>>     return var;
>> }
>>
>> void bar(inout T)(T a, T b)
>> {
>>     a = b;
>> }
>>
>> void main()
>> {
>>     const int con;
>>     immutable int imm;
>>
>>     foo(con, con); // OK (returns const int)
>>     foo(con, imm); // Error: returned inout(T) is ambiguous
>>
>>     bar(con, con); // OK
>>     bar(con, imm); // OK (no ambiguity because inout not used)
>> }
>>
>
> Another approach, if we wanted a different syntax, would be to add a new keyword 'mutable'. Like immutable overrides const, mutable would override both const and immutable:
>
> static assert(is(mutable(const(int)) == int));
> static assert(is(mutable(immutable(**int)) == int));
>
> Here's how it would look:
>
> T foo(T)(mutable(T) a, mutable(T) b)
> {
>     a = b = 123;
>     return a;
>
> }
>
> void main()
> {
>     const int con;
>     immutable int imm;
>
>     foo(con, con); // OK (returns const int)
>     foo(con, imm); // Error: T is ambiguous
> }
>


July 06, 2013
On Saturday, 6 July 2013 at 13:30:02 UTC, TommiT wrote:
> Some more details of this proposed feature:
>
> inout(T) foo(inout T)(T a, T b)
> {
>     return var;
> }
>
> void bar(inout T)(T a, T b)
> {
>     a = b;
> }
>
> void main()
> {
>     const int con;
>     immutable int imm;
>
>     foo(con, con); // OK (returns const int)
>     foo(con, imm); // Error: returned inout(T) is ambiguous
>
>     bar(con, con); // OK
>     bar(con, imm); // OK (no ambiguity because inout not used)
> }

Some more details of a bit more complicated use case:

E foo(inout T : E[3], E)(T arr)
{
    return arr[0];
}

E bar(inout T : inout(E)[3], E)(T arr)
{
    return arr[0];
}

void main()
{
    immutable int[3] iarr;

    foo(iarr); // returns an immutable int
    bar(iarr); // returns an int
}
July 06, 2013
On 07/06/2013 04:06 PM, Manu wrote:
> ...
>     It seems that your code works if you put the Template Type explicit:
>     ----
>     import std.stdio;
>     import std.traits : Unqual;
>
>     void foo(T)(Unqual!T a) {
>              writeln(typeof(a).stringof, " <-> ", T.stringof);
>     }
>
>     void main() {
>              int a;
>              const int b;
>              immutable int c;
>
>              //foo(c); /// Error
>              foo!(typeof(a))(a);
>              foo!(typeof(b))(b);
>              foo!(typeof(c))(c);
>     }
>     ----
>
>
> Indeed, hence my point that the type deduction is the key issue here.
> It should be possible... maybe a bit tricky though.

The key issue is that the syntax void foo(T)(Unqual!T a); denotes roughly the opposite of what you think it denotes. Basically, inference is instructed to find a T, such that Unqual!T is the argument type.

July 06, 2013
On 07/06/13 13:35, Manu wrote:
> The point here is that I want more control over the signature of the instantiated template (reduce the number of permutations generated for various calls). Template blow-out is perhaps the biggest and most well known day-to-day problem in C++, and tools like this may be very valuable to mitigate the disaster.

I was going to reply that while template bloat is a real issue, it is the
compiler that should deal with this (by always inlining trivial function).
Then I remembered that you mentioned that you want this only for "primitive"
types, and realized what your real problem is. IFTI and value types.
For example:

      auto f(T...)(T a) {/*...*/}

      int a;
      const int b;
      immutable int c;
      f(a);
      f(b);
      f(c);

This creates three instances of 'f'. And it gets much worse with a larger number of arguments... Typically all instantiations will be identical, except in cases where the 'f' implementation tries to mutate the argument(s). (This mutation is always safe when T is passed by value and T does not contain external references)

IFTI should just strip the qualifiers from all value-passed POD types
which contain no refs (ie no pointers, slices and classes inside). Doing
that will significantly reduce template bloat, at practically no
cost. It *is* a change in behavior, but there shouldn't be much code
out there that sensibly relies on the 1:1 type propagation. Explicitly
specifying a type will still be possible using 'typeof'.

Note that this is already done for pointers; calling 'f' with the following:

      immutable(void)* a;
      const immutable(void)* b;
      immutable immutable(void)* c;

will not create three separate instances.
This is possible in the pointer case because the compiler can get away
with manipulating the type. Doing this for structs would be more
complicated; fixing the primitive types- (and maybe PODs) case isn't.

BTW, the way D's 'auto' works contributes to the problem:

      const int a;
      auto b = a+a;
      auto c = a+1;
      f(b);
      f(c);

uses two different 'f' instances.

artur
July 06, 2013
On Saturday, 6 July 2013 at 15:05:51 UTC, Artur Skawina wrote:
> ...

It is not that simple. Consider:

void f(T)(T t)
{
    static if (T == Unqual!T)
    // one function body
    else
    // completely different one
}

Currently every template instance is completely independent and tied to exact type. I don't know of any tool to express "group of related types" concept in D other than built-in "inout".
July 06, 2013
On Saturday, 6 July 2013 at 01:35:09 UTC, Manu wrote:
> ...

If this is about template bloat I think much better is to address problem in general. For example, internal linkage or strict export requirements - anything that will allow to inline and completely eliminate trivial templates leaving no traces of it in final executable.