Thread overview
Creating ranges over mutable, const, or immutable data structures.
May 24, 2014
w0rp
May 24, 2014
Ali Çehreli
May 24, 2014
w0rp
Jun 03, 2014
Ali Çehreli
May 25, 2014
Ali Çehreli
May 24, 2014
I have been writing my own hashmap which can provide forward ranges usable in @safe pure nothrow functions, because it's going to be useful for creating graph data structures with the same. I came to writing my ranges and I figured out how to do everything right for just mutable hashmaps, but I have no idea how to manage the tail-const nature of ranges in a variety of combinations. I figured out that I need to satisfy the following constraints.

Create a mutable KeyRange over a map which forwards on the right constness for the key type, so the following must be true.

HashMap!(K, V).keys.front -> K
const(HashMap!(K, V)).keys.front -> const(K)
immutable(HashMap!(K, V)).keys.front -> immutable(K)

I have encounted some difficulty in trying to write a range which does this.
May 24, 2014
On 05/24/2014 10:02 AM, w0rp wrote:

> I have been writing my own hashmap which can provide forward ranges
> usable in @safe pure nothrow functions, because it's going to be useful
> for creating graph data structures with the same. I came to writing my
> ranges and I figured out how to do everything right for just mutable
> hashmaps, but I have no idea how to manage the tail-const nature of
> ranges in a variety of combinations. I figured out that I need to
> satisfy the following constraints.
>
> Create a mutable KeyRange over a map which forwards on the right
> constness for the key type, so the following must be true.
>
> HashMap!(K, V).keys.front -> K
> const(HashMap!(K, V)).keys.front -> const(K)
> immutable(HashMap!(K, V)).keys.front -> immutable(K)
>
> I have encounted some difficulty in trying to write a range which does
> this.

How timely! :) Jonathan Crapuchettes talked about facing the same issue in his talk at DConf. Perhaps he will post his WrapMutability template here or you can wait for his talk on YouTube.

Ali

May 24, 2014
On Saturday, 24 May 2014 at 18:01:43 UTC, Ali Çehreli wrote:
> On 05/24/2014 10:02 AM, w0rp wrote:
>
> > I have been writing my own hashmap which can provide forward
> ranges
> > usable in @safe pure nothrow functions, because it's going to
> be useful
> > for creating graph data structures with the same. I came to
> writing my
> > ranges and I figured out how to do everything right for just
> mutable
> > hashmaps, but I have no idea how to manage the tail-const
> nature of
> > ranges in a variety of combinations. I figured out that I
> need to
> > satisfy the following constraints.
> >
> > Create a mutable KeyRange over a map which forwards on the
> right
> > constness for the key type, so the following must be true.
> >
> > HashMap!(K, V).keys.front -> K
> > const(HashMap!(K, V)).keys.front -> const(K)
> > immutable(HashMap!(K, V)).keys.front -> immutable(K)
> >
> > I have encounted some difficulty in trying to write a range
> which does
> > this.
>
> How timely! :) Jonathan Crapuchettes talked about facing the same issue in his talk at DConf. Perhaps he will post his WrapMutability template here or you can wait for his talk on YouTube.
>
> Ali

I thought someone would say something like that! I'll wait for the talk I suppose.
May 25, 2014
On 05/24/2014 10:02 AM, w0rp wrote:

> Create a mutable KeyRange over a map which forwards on the right
> constness for the key type, so the following must be true.
>
> HashMap!(K, V).keys.front -> K
> const(HashMap!(K, V)).keys.front -> const(K)
> immutable(HashMap!(K, V)).keys.front -> immutable(K)
>
> I have encounted some difficulty in trying to write a range which does
> this.

'this' template parameters is useful in some cases. Although the 'this' parameters are not actually referenced in the following programs they do templatize the member function with the type of the current object.

This is a simple test that demonstrates that the mutability of the object is transferred to the front of a range that it returns:

import std.algorithm;

struct S
{
    int[5] s;

    auto opSlice(this This)()
    {
        return s[].filter!(a => a % 2);
    }
}

void main()
{
    auto s = S();
    auto r = s[];
    static assert (is (typeof(r.front) == int));

    auto sc = const(S)();
    auto rc = sc[];
    static assert (is (typeof(rc.front) == const(int)));

    auto si = immutable(S)();
    auto ri = si[];
    static assert (is (typeof(ri.front) == immutable(int)));
}

The following is closer to your example (but don't pay attention to the "hash map" implementation :p) :

import std.stdio;
import std.algorithm;
import std.array;
import std.exception;

// This can be sophisticated as well
struct Keys(Range)
{
    Range range;

    bool empty() const @property
    {
        return range.empty;
    }

    auto front() @property
    {
        return range.front;
    }

    void popFront()
    {
        range.popFront();
    }
}

auto makeKeyRange(Range)(Range range)
{
    return Keys!Range(range);
}

struct HashMap(K, V)
{
    // Stupid implementation
    K[] keys_;
    V[] values_;

    this(K key, V value) immutable
    {
        K[] makeKeysInit()
        {
            K[] keysInit;
            keysInit ~= key;
            return keysInit;
        }

        K[] localKeys = makeKeysInit();
        keys_ = localKeys.assumeUnique;
    }

    auto keys(this This)()
    {
        return makeKeyRange(keys_);
    }
}

void main()
{
    // Some test types
    alias K = int[];
    alias V = double[];
    alias HM = HashMap!(K, V);

    {
        auto hm = HM([ 1, 2 ], [ 1.1, 2.2 ]);
        auto r = hm.keys;
        static assert (is (typeof(r.front) == int[]));
    }

    {
        auto hm = const(HM)([ 1, 2 ], [ 1.1, 2.2 ]);
        auto r = hm.keys;
        static assert (is (typeof(r.front) == const(int[])));
    }

    {
        auto hm = immutable(HM)([ 1, 2 ], [ 1.1, 2.2 ]);
        auto r = hm.keys;
        static assert (is (typeof(r.front) == immutable(int[])));
    }
}

Ali

June 03, 2014
On 05/24/2014 11:01 AM, Ali Çehreli wrote:
> On 05/24/2014 10:02 AM, w0rp wrote:
>
>  > I have been writing my own hashmap which can provide forward ranges
>  > usable in @safe pure nothrow functions, because it's going to be useful
>  > for creating graph data structures with the same. I came to writing my
>  > ranges and I figured out how to do everything right for just mutable
>  > hashmaps, but I have no idea how to manage the tail-const nature of
>  > ranges in a variety of combinations. I figured out that I need to
>  > satisfy the following constraints.
>  >
>  > Create a mutable KeyRange over a map which forwards on the right
>  > constness for the key type, so the following must be true.
>  >
>  > HashMap!(K, V).keys.front -> K
>  > const(HashMap!(K, V)).keys.front -> const(K)
>  > immutable(HashMap!(K, V)).keys.front -> immutable(K)
>  >
>  > I have encounted some difficulty in trying to write a range which does
>  > this.
>
> How timely! :) Jonathan Crapuchettes talked about facing the same issue
> in his talk at DConf. Perhaps he will post his WrapMutability template
> here or you can wait for his talk on YouTube.
>
> Ali
>

That presentation is published. WrapMutability appears at 32:55:

  http://www.ustream.tv/recorded/47930242

Ali