November 15, 2012
On 11/15/2012 10:26 AM, Joseph Rushton Wakeling wrote:
> On 11/15/2012 06:40 PM, Ali Çehreli wrote:
>> b) The user wants to play safe:
>>
>> auto makeFoo()
>> {
>> Foo foo;
>> foreach (i; 0..10)
>> foo.add( /* new data point */ );
>> return foo;
>> }
>>
>> void main()
>> {
>> immutable foo = makeFoo();
>> }
>>
>> Both of those compile with dmd 2.060.
>
> Really? I'm using from-GitHub dmd, and with the above example I get a
> "cannot implicitly convert expression to immutable" error, e.g.:
>
> Error: cannot implicitly convert expression (testSets(nfRaw,0.1L)) of
> type TestData!(ulong,ulong) to immutable(TestData!(ulong,ulong))
>

The following program compiles without any errors with dmd 2.060:

struct Foo(T0, T1)
{
    T0 t0;
    T1 t1;
}

auto testSets(T0, T1)(T0 t0, T1 t1)
{
    auto foo = Foo!(T0, T1)(t0, t1);
    return foo;
}

void main()
{
    ulong nfRaw;
    immutable foo = testSets(nfRaw,0.1L);
}

So far it makes sense to me: There shouldn't be any problem with making a copy of a value type and marking that copy as immutable.

Unless there exists a member that would make this unsafe. Let's add an int[] member to Foo:

struct Foo(T0, T1)
{
    T0 t0;
    T1 t1;
    int[] a;
}

auto testSets(T0, T1)(T0 t0, T1 t1, int[] a)
{
    auto foo = Foo!(T0, T1)(t0, t1, a);
    return foo;
}

void main()
{
    ulong nfRaw;
    int[] a = [ 42 ];
    immutable foo = testSets(nfRaw, 0.1L, a); // <-- compilation error
    assert(foo.a[0] == 42);
    a[0] = 43;
    assert(foo.a[1] == 43);  // <-- if compiled, this would be a bug
}

Do you have a reference type in your struct?

Ali
November 15, 2012
On 11/15/2012 07:48 PM, Ali Çehreli wrote:
> Do you have a reference type in your struct?

Yes -- there are associative arrays, and some of those contain dynamic arrays.

November 15, 2012
On 11/15/2012 10:48 AM, Ali Çehreli wrote:

> assert(foo.a[0] == 42);
> a[0] = 43;
> assert(foo.a[1] == 43); // <-- if compiled, this would be a bug

Of course I meant foo.a[0] on the last line above as well.

Ali

November 15, 2012
On 11/15/2012 07:48 PM, Ali Çehreli wrote:
> Do you have a reference type in your struct?

Assuming I do, what can I do to ensure the struct instance is immutable?  Is cast(immutable) now the only option?
November 16, 2012
On Thursday, 15 November 2012 at 23:40:16 UTC, Joseph Rushton Wakeling wrote:
> On 11/15/2012 07:48 PM, Ali Çehreli wrote:
>> Do you have a reference type in your struct?
>
> Assuming I do, what can I do to ensure the struct instance is immutable?  Is cast(immutable) now the only option?

This code works with dmd git head (and might work with dmd 2.060).

struct Foo
{
    int a, b;
    string[string] aa;
}
immutable(Foo) makeFoo() pure
{
    Foo foo;
    foo.aa["a"] = "hello";
    foo.aa["b"] = "world";
    return foo;
    // compiler allows this implicit casting from Foo to immutable(Foo),
    // because compiler can guarantee the instance 'foo' doesn't have
    // mutable indirections to any global data so makeFoo is a pure function.
}
void main()
{
    immutable ifoo = makeFoo();
}

Kenji Hara
November 16, 2012
On 11/15/2012 07:43 PM, Kenji Hara wrote:

> This code works with dmd git head (and might work with dmd 2.060).

Yes, it works with dmd 2.060 as well.

> immutable(Foo) makeFoo() pure

I would like to repeate an earlier question: Does makeFoo() want that the returned object be immutable? If so, the previous signature is required.

However, if makeFoo() does not care, then it would be better if it returned a mutable Foo:

Foo makeFoo() pure

In that case the callers could decide whether they wanted to have the returned object as mutable or immutable:

    immutable ifoo = makeFoo();
    auto mfoo = makeFoo();

The above works because makeFoo() is pure.

If makeFoo() were not pure, and in general, Foo may need to provide an .idup member function:

import std.conv;
import std.exception;

struct Foo
{
    int a, b;
    string[string] aa;
    int[] slice;

    immutable(Foo) idup() pure const @property
    {
        auto copy = to!(string[string])(aa);
        immutable iaa = assumeUnique(copy);
        return immutable(Foo)(a, b, iaa, slice.idup);
    }
}

void main()
{
    auto foo = Foo(42, 43, [ "a" : "hello", "b" : "world" ], [ 42 ]);
    immutable ifoo = foo.idup;
}

Ali

November 16, 2012
On 11/16/2012 05:51 AM, Ali Çehreli wrote:
> However, if makeFoo() does not care, then it would be better if it returned a
> mutable Foo:
>
> Foo makeFoo() pure
>
> In that case the callers could decide whether they wanted to have the returned
> object as mutable or immutable:
>
>      immutable ifoo = makeFoo();
>      auto mfoo = makeFoo();
>
> The above works because makeFoo() is pure.

Unfortunately in general that's a no-go as some of the generation functions involve reading from outside files, and some involve random number generation (though I'm curious to see the result of bearophile's pure RNG).

> If makeFoo() were not pure, and in general, Foo may need to provide an .idup
> member function:
>
> import std.conv;
> import std.exception;
>
> struct Foo
> {
>      int a, b;
>      string[string] aa;
>      int[] slice;
>
>      immutable(Foo) idup() pure const @property
>      {
>          auto copy = to!(string[string])(aa);
>          immutable iaa = assumeUnique(copy);
>          return immutable(Foo)(a, b, iaa, slice.idup);
>      }
> }

I'll have a look into this.  The trouble is that it's not such a simple structure: it's actually more like,

struct Data
{
    Node[size_t] nodes;
}

struct Node
{
    size_t id;
    size_t[];
}

... is it possible to just do

    auto copy = to!(Node[size_t])(nodes);
    immutable inodes = assumeUnique(copy);

or would I have to go further recursively into Node?  (Or, alternatively, will assumeUnique pick up on any idup method I define for Node?)
November 16, 2012
On 11/16/2012 12:55 PM, Joseph Rushton Wakeling wrote:
> The trouble is that it's not such a simple  structure: it's actually more like,

I should add that I'm not trying to be coy about revealing my code; I'm happy to do so, but as it's a rather long file I don't want to oblige anyone to have to read through it.

November 27, 2012
On 11/16/2012 05:51 AM, Ali Çehreli wrote:
> If makeFoo() were not pure, and in general, Foo may need to provide an .idup
> member function:

I've been trying this out and ran into some problems with the to!()() conversion.

Here's a concrete example.  Suppose I have a couple of structs which are designed respectively to represent nodes in a network, and a collection of those nodes:

/////////////////////////////////////////////////////////////////////// alias Tuple!(uint, "id") Link;

struct Node
{
       uint id;
       Link[] links;

       void addLink(uint l)
       {
             links ~= Link(l);
       }

       immutable(Node) idup() pure const @property
       {
             auto linkCopy = to!(Link[])(links);
             immutable ilinks = assumeUnique(linkCopy);
             return immutable(Node)(id, ilinks);
       }
}


struct Network
{
       Node[uint] nodes;

       void add(uint i, uint j)
       {
             if((i in nodes) is null)
                   nodes[i] = Node(i);
             if((j in nodes) is null)
                   nodes[j] = Node(j);

             nodes[i].addLink(j);
             nodes[j].addLink(i);
       }

       void print()
       {
             foreach(k; nodes.keys)
             {
                   write("[", k, "]");
                   foreach(l; nodes[k].links)
                         write(" ", l.id);
                   writeln();
             }
             writeln();
       }
}
///////////////////////////////////////////////////////////////////////

Now, the idup() command for Node works just fine:

       auto n1 = Node(1);

       n1.addLink(5);
       n1.addLink(6);
       writeln(n1.links);

       immutable n2 = n1.idup;
       writeln(n2.links);

...  but if I try to introduce a similar function for the Network struct,

       immutable(Network) idup() pure const @property
       {
             auto nodeCopy = to!(Node[uint])(nodes);
             immutable imnodes = assumeUnique(nodeCopy);
             return immutable(Network)(imnodes);
       }

it fails to compile with an error relating to the to!(Node[uint])() conversion:

---------------------------------------------------------------------------------
/opt/dmd/include/d2/std/conv.d(269): Error: template std.conv.toImpl does not
match any function template declaration. Candidates are:
/opt/dmd/include/d2/std/conv.d(325):        std.conv.toImpl(T, S)(S value) if
(isImplicitlyConvertible!(S, T) && !isEnumStrToStr!(S, T) && !isNullToStr!(S, T))
/opt/dmd/include/d2/std/conv.d(431):        std.conv.toImpl(T, S)(ref S s) if
(isRawStaticArray!(S))
/opt/dmd/include/d2/std/conv.d(445):        std.conv.toImpl(T, S)(S value) if
(is(S : Object) && !is(T : Object) && !isSomeString!(T) && hasMember!(S, "to")
&& is(typeof(S.init.to!(T)()) : T))
/opt/dmd/include/d2/std/conv.d(466):        std.conv.toImpl(T, S)(S value) if
(is(typeof(S.init.opCast!(T)()) : T) && !(isSomeString!(T) && !is(T == enum) &&
!isAggregateType!(T)))
/opt/dmd/include/d2/std/conv.d(497):        std.conv.toImpl(T, S)(S value) if
(!isImplicitlyConvertible!(S, T) && is(T == struct) && is(typeof(T(value))))
/opt/dmd/include/d2/std/conv.d(269):        ... (16 more, -v to show) ...
/opt/dmd/include/d2/std/conv.d(325): Error: template std.conv.toImpl cannot
deduce template function from argument types !(Node)(const(Node))
/opt/dmd/include/d2/std/conv.d(269): Error: template instance toImpl!(Node)
errors instantiating template
/opt/dmd/include/d2/std/conv.d(1387): Error: template instance
std.conv.to!(Node).to!(const(Node)) error instantiating
/opt/dmd/include/d2/std/conv.d(269):        instantiated from here:
toImpl!(Node[uint], const(Node[uint]))
inodes.d(41):        instantiated from here: to!(const(Node[uint]))
/opt/dmd/include/d2/std/conv.d(269): Error: template instance
std.conv.toImpl!(Node[uint], const(Node[uint])) error instantiating
inodes.d(41):        instantiated from here: to!(const(Node[uint]))
inodes.d(41): Error: template instance
std.conv.to!(Node[uint]).to!(const(Node[uint])) error instantiating
---------------------------------------------------------------------------------

I'm guessing this means I have to define a custom opCast (for Node, I guess) but the documentation on how to do so seems sparse -- can you advise?

Full code example attached.

Thanks & best wishes,

      -- Joe


November 27, 2012
On 11/27/2012 01:16 PM, Joseph Rushton Wakeling wrote:
>        immutable(Node) idup() pure const @property
>        {
>              auto linkCopy = to!(Link[])(links);
>              immutable ilinks = assumeUnique(linkCopy);
>              return immutable(Node)(id, ilinks);
>        }

Actually I'm being overly complicated here as with dynamic arrays I can simply do,

        immutable(Node) idup() pure const @property
        {
              return immutable(Node)(id, links.idup);
        }

... so the real issue here seems to be that there's no canonical way (that I can find) to idup an _associative_ array.