Thread overview
Defining an alias to an overloaded function
Jan 20, 2020
olvy
Jan 21, 2020
Boris Carvajal
Jan 23, 2020
olvy
January 20, 2020
I'm learning D, and as an exercise, I'm trying to define a HashSet that would be a wrapper around an associative array with some dummy value type.

This worked fine until I've tried writing opSlice for this HashSet in terms of the byKey() function of the AA.  I defined my own internal type that wraps the one returned by byKey and returned it.  However I had to keep byKey's result inside my internal type, but I couldn't figure out how to name it properly without getting a compilation error.

The problem is that byKey is overloaded and but I can't figure out how to convince the compiler to disambiguate between the overloads.

Another lesser problem is that I have to write "opSlice" explicitly at the call site instead of using [].   But it might be that the compiler is confused due to failure of parsing the function.

My aim isn't building a HashSet, the point is learning, here specifically learning how to convince the compiler to use the correct byKey overload in this case.
I could also trivially use the AA's keys() function to implement opSlice.  But I like byKey since it exposes just an iterator, while keys() actually allocates an array which isn't necessary here.

I'm using dmd 2.089 on my machine.

My code is (also in https://wandbox.org/permlink/zXsf7sQiKDj6zjlJ)

import std;

struct HashSet(T) {
    int[T] _dict;

    bool add(T val) {
        auto prev = val in _dict;
        _dict[val] = 0;
        return prev is null;
    }

    bool remove(T val) {
        return _dict.remove(val);
    }

    bool opBinaryRight(string op)(T val) {
        static if (op == "in") {
            return (val in _dict) !is null;
        } else {
            static assert(false, "Operator " ~ op ~ " not implemented");
        }
    }

    import std.traits;
    import std.meta;
    struct RangeImpl(T) {
        // Next line has compilation error since byKey has 2 overloads
        alias byKeyAlias = Alias!(byKey!(int[T])(T.init));
        ReturnType!(byKeyAlias) keyRange;
        // alias byKeyAlias = byKey;
        // ReturnType!(byKey!(int[T], T, int)) keyRange;
        this(ref int[T] d) {
            keyRange = d.byKey();
        }
        bool empty() const {
            return keyRange.empty;
        }
        ref T front() {
            return keyRange.front;
        }
        void popFront() {
            keyRange.popFront();
        }
    }

    // T[] opSlice(T)() {
    //     return _dict.keys;
    // }

    RangeImpl!T opSlice(T)() {
        return RangeImpl!T(_dict);
    }

}

void main()
{
    HashSet!long h;
    h.add(0);
    h.add(1);
    if (1 in h) {
        writeln("it is");
    }
    if (h.opSlice!long.all!(nn => nn < 5)) {
        writeln("less than 5");
    }
}
January 21, 2020
On Monday, 20 January 2020 at 22:02:54 UTC, olvy wrote:
> I'm learning D, and as an exercise, I'm trying to define a HashSet that would be a wrapper around an associative array with some dummy value type.

This seems to work:

...
    struct RangeImpl(T) {
        alias byKeyRetType = typeof(byKey!(int[T])((int[T]).init));
        byKeyRetType keyRange;
        this(ref int[T] d) {
            keyRange = d.byKey;
        }
        bool empty() {
            return keyRange.empty;
        }
        ...
    }

    RangeImpl!T opSlice() {
        return RangeImpl!T(_dict);
    }
}

void main()
{
    ...
    if (h[].all!(nn => nn < 5)) {
        writeln("less than 5");
    }

January 23, 2020
On Tuesday, 21 January 2020 at 04:44:43 UTC, Boris Carvajal wrote:
> This seems to work:
>
> ...
>     struct RangeImpl(T) {
>         alias byKeyRetType = typeof(byKey!(int[T])((int[T]).init));
>         byKeyRetType keyRange;
>         this(ref int[T] d) {
>             keyRange = d.byKey;
>         }
>         bool empty() {
>             return keyRange.empty;
>         }
>         ...
>     }
>
>     RangeImpl!T opSlice() {
>         return RangeImpl!T(_dict);
>     }
> }
>
> void main()
> {
>     ...
>     if (h[].all!(nn => nn < 5)) {
>         writeln("less than 5");
>     }

Thank you very much!  I have completely forgotten about typeof().

I see you also fixed my unnecessary (T) template parameter to opSlice, which also caused a compilation error in addition to the first mistake.  I think this is because it "shadows" the original T parameter from the struct itself, right?