Thread overview
KeyType, ValueType traits for hashes
Jan 23, 2012
Andrej Mitrovic
Jan 24, 2012
bearophile
Jan 24, 2012
Andrej Mitrovic
Jan 24, 2012
Dejan Lekic
Jan 24, 2012
Andrej Mitrovic
Jan 24, 2012
Mantis
Jan 24, 2012
bearophile
Jan 24, 2012
Mantis
Jan 25, 2012
Andrej Mitrovic
Jan 25, 2012
Jonathan M Davis
January 23, 2012
I haven't found these in Phobos, so if they're really not in std.traits or anywhere do you think I should make a pull request for this?

import std.traits;

template KeyType(T)
    if (isAssociativeArray!T)
{
    alias typeof(T.keys()[0]) KeyType;
}

template ValueType(T)
    if (isAssociativeArray!T)
{
    alias typeof(T.values()[0]) ValueType;
}

void main()
{
    alias string[int] Hash;
    static assert(is(KeyType!Hash == int));
    static assert(is(ValueType!Hash == string));
}
January 24, 2012
Andrej Mitrovic:

> I haven't found these in Phobos, so if they're really not in std.traits or anywhere do you think I should make a pull request for this?

They are useful.

Bye,
bearophile
January 24, 2012
But I did implement them poorly, this is better:

import std.traits;

template KeyType(AA)
    if (isAssociativeArray!AA)
{
    static if (is(AA V : V[K], K))
    {
        alias K KeyType;
    }
}

template ValueType(AA)
    if (isAssociativeArray!AA)
{
    static if (is(AA V : V[U], U))
    {
        alias V ValueType;
    }
}
January 24, 2012
The only issue I have with this is the isAssociativeArray constraint. That template uses __traits(isAssociativeArray, T), which returns FALSE for DCollection's HashMap type. It would be a shame if you couldn't define a drop-in replacement to builtin hashes with your own implementation, seeing as such a type wouldn't work with functions in Phobos that take a hash (actually right now very few functions in Phobos do take a hash, if any..).
January 24, 2012
Andrej, I agree, they should be in std.traits .

January 24, 2012
24.01.2012 8:43, Andrej Mitrovic пишет:
> But I did implement them poorly, this is better:
>
> import std.traits;
>
> template KeyType(AA)
>      if (isAssociativeArray!AA)
> {
>      static if (is(AA V : V[K], K))
>      {
>          alias K KeyType;
>      }
> }
>
> template ValueType(AA)
>      if (isAssociativeArray!AA)
> {
>      static if (is(AA V : V[U], U))
>      {
>          alias V ValueType;
>      }
> }
>
Might I suggest that it be implemented this way:

template KeyType(AA)
{
    static if (is(AA V : V[K], K))
    {
        alias K KeyType;
    }
    else static assert( 0, "Not associative array: " ~ AA.stringof );
}


Of course, most likely that user already did type check, but if not, this will give less cryptic error:

Error: static assert "Not associative array: int"
instantiated from here: KeyType!(int)

, instead of:

Error: template instance KeyType!(int) KeyType!(int) does not match template declaration KeyType(AA) if (isAssociativeArray!(AA))
January 24, 2012
Mantis:

> Of course, most likely that user already did type check, but if not, this will give less cryptic error:
> 
> Error: static assert "Not associative array: int"
> instantiated from here: KeyType!(int)
> 
> , instead of:
> 
> Error: template instance KeyType!(int) KeyType!(int) does not match template declaration KeyType(AA) if (isAssociativeArray!(AA))

It's a tradeoff. Your custom error message is more readable, but the failure at the template constraint causes a error line at the instantiation point. Sadly I think there is no solution that solves both problems  (we have stack traces for templates, but...).

Bye,
bearophile
January 24, 2012
24.01.2012 20:49, bearophile пишет:
> Mantis:
>
>> Of course, most likely that user already did type check, but if not,
>> this will give less cryptic error:
>>
>> Error: static assert "Not associative array: int"
>> instantiated from here: KeyType!(int)
>>
>> , instead of:
>>
>> Error: template instance KeyType!(int) KeyType!(int) does not match
>> template declaration KeyType(AA) if (isAssociativeArray!(AA))
> It's a tradeoff. Your custom error message is more readable, but the failure at the template constraint causes a error line at the instantiation point. Sadly I think there is no solution that solves both problems  (we have stack traces for templates, but...).
>
> Bye,
> bearophile
>
True. Maybe something is possible to achieve in this direction?:

template verboseFail(alias Cond, string message)
{
    static if (Cond)
    {
        enum verboseFail = true;
    }
    else
    {
        pragma( msg, message );
        enum verboseFail = false;
    }
}

template KeyType(AA)
    if (verboseFail!(isAssociativeArray!AA, "Not assosiative array: " ~ AA.stringof))
{
    static if (is(AA V : V[K], K))
    {
        alias K KeyType;
    }
}

It doesn't work very nicely (the message is logged, but dos not look as it is related to error), but perharps it may be improved somehow?
January 25, 2012
Unfortunately you would have to do that with every template in order to be consistent, and that's too much work. The real improvement would be for the compiler to let us know *which* of the constraints failed, e.g.:

alias KeyType!(int[]) Key;

Error: Can't instantiate KeyType!(int) due to failed constraint: if
(isAssociativeArray!(AA))

I don't know if there's an actual enhancement request for this but I think it was probably discussed before.
January 25, 2012
On Wednesday, January 25, 2012 01:24:59 Andrej Mitrovic wrote:
> Unfortunately you would have to do that with every template in order to be consistent, and that's too much work. The real improvement would be for the compiler to let us know *which* of the constraints failed, e.g.:
> 
> alias KeyType!(int[]) Key;
> 
> Error: Can't instantiate KeyType!(int) due to failed constraint: if
> (isAssociativeArray!(AA))
> 
> I don't know if there's an actual enhancement request for this but I think it was probably discussed before.

It's been discussed. I believe that question is how it would be implemented. At present, the template constraint is basically just a condition that's true or false. For it to pass, which pieces of it are true or false doesn't matter as long as the whole is true. And it becomes far more complicated when there are multiple template constraints, any one of them which could be true - though the likely solution to that is just to do what the compiler currently does are report failure on the first one.

I believe that Andrei is among those who want a feature along those lines, so there's a decent chance that we'll see it at some point, but given the more critical stuff that needs to be completed (e.g. TDPL compliance), I doubt that it's a high priority at the moment.

Regardless, the correct thing to do in most cases is to use template constraints, not static ifs. Using static ifs is far less flexible (and, as Bearophile points out, worse with error reporting as far as file and line number go). The solution in this case is to improve the compiler. The template constraint is often enough to tell what's wrong by itself anyway (and it definitely is in this case). Regardless, the current situation is vastly better than that of C++.

- Jonathan M Davis