Thread overview
Allow const/immutable for normal functions, inner functions and delegates?
Feb 19, 2014
John Colvin
Feb 19, 2014
John Colvin
Feb 19, 2014
Timon Gehr
Feb 20, 2014
Meta
February 19, 2014
Good idea/bad idea/already done?

int ga = 1;
const int gb = 2;
immutable int gc = 3;

int foo() immutable
{
    // reference to gc is legal
    // any reference to ga or ba is an error
}
int bar() const
{
    // reference to ga, gb and gc is legal
    // cannot modify ga (or gb or gc, obviously)
}

void main()
{
    int a = 1;
    const int b = 2;
    immutable int c = 3;

    int innerFoo() immutable
    {
        // reference to c or gc is legal
        // any reference to a, ga, b or ba is an error
    }
    int innerBar() const
    {
        // reference to a, ga, b, gb c and gc is legal
        // cannot modify a or ga
    }
}

For the inner functions, it's as if the const/immutable is applied to the context pointer, the same as it is applied to "this" in a class/struct method.

This would be useful because it:
a) provides better control over the exact behaviour of functions w.r.t. global/outer state.

b) More importantly: functions/delegates marked immutable can be freely shared across threads! This could enable some cool stuff

e.g. this example using an immutable closure to offload work to a worker without any explicit handling of input data in the worker thread:

auto makeJob(int i, int[] arr)
{
    auto arrImm = arr.idup;
    immutable int iImm = i;

    int[] inner() immutable
    {
        return arr.map!((x) => x % iImm).array;
    }
    return &inner;
}

void worker(Tid tid)
{
    receive(
        (int[] delegate() work)
        {
            tid.send(work());
        }
    );
};

void main()
{
    auto tid = spawn(&worker, thisTid);

    tid.send(makeJob(3, [1,2,3,4,5]));
    receive(
        (int[] data)
        {
            assert(data == [1,2,0,1,2]);
        }
    );
}

All the worker needs to know about its workload is the return type of the delegate :) Batteries included!

Note: functions marked immutable are implicitly pure, allowing their return values to be implicitly cast to immutable.
February 19, 2014
On Wednesday, 19 February 2014 at 22:20:45 UTC, John Colvin wrote:

> e.g. this example using an immutable closure to offload

That should be

> e.g. this example using a closure marked immutable to offload


Don't want to add any confusion there.
February 19, 2014
On 02/19/2014 11:20 PM, John Colvin wrote:
> Good idea/bad idea/already done?
>
> int ga = 1;
> const int gb = 2;
> immutable int gc = 3;
>
> int foo() immutable
> {
>      // reference to gc is legal
>      // any reference to ga or ba is an error
> }
> int bar() const
> {
>      // reference to ga, gb and gc is legal
>      // cannot modify ga (or gb or gc, obviously)
> }
> ...

Does not work as there is no context pointer to qualify and it would add strange limitations to const/... member functions and/or language inconsistencies.

> void main()
> {
>      int a = 1;
>      const int b = 2;
>      immutable int c = 3;
>
>      int innerFoo() immutable
>      {
>          // reference to c or gc is legal
>          // any reference to a, ga, b or ba is an error
>      }
>      int innerBar() const
>      {
>          // reference to a, ga, b, gb c and gc is legal
>          // cannot modify a or ga
>      }
> }
>
> For the inner functions, it's as if the const/immutable is applied to
> the context pointer, the same as it is applied to "this" in a
> class/struct method.
>
> This would be useful because it:
> a) provides better control over the exact behaviour of functions w.r.t.
> global/outer state.
> ...

I've brought this up a few times too. This _must_ be done. The current behaviour of DMD is unacceptable here.

February 20, 2014
On Wednesday, 19 February 2014 at 22:20:45 UTC, John Colvin wrote:
> int ga = 1;
> const int gb = 2;
> immutable int gc = 3;
>
> int foo() immutable
> {
>     // reference to gc is legal
>     // any reference to ga or ba is an error
> }

Doesn't pure already do this?