Jump to page: 1 2 3
Thread overview
Best practices for logical const
Feb 15, 2014
Adam D. Ruppe
Feb 15, 2014
Jonathan M Davis
Feb 15, 2014
Jakob Ovrum
Feb 15, 2014
Peter Alexander
Feb 15, 2014
Jesse Phillips
Feb 15, 2014
Timon Gehr
Feb 15, 2014
Meta
Feb 15, 2014
Meta
Feb 16, 2014
Meta
Feb 16, 2014
Meta
Feb 16, 2014
Meta
Feb 16, 2014
Meta
Feb 16, 2014
Meta
Feb 16, 2014
Jonathan M Davis
Feb 16, 2014
Stanislav Blinov
Feb 16, 2014
Jonathan M Davis
Feb 16, 2014
Stanislav Blinov
Feb 16, 2014
Jonathan M Davis
Feb 16, 2014
Dicebot
February 15, 2014
D doesn't have logical const, but sometimes it is useful, especially with lazy initialization of a field, and we can kinda fake it with casts or with global variables. Modifying an immutable object is undefined behavior, so how can we avoid that, and if not, try to minimize the problems in practice?

Using global variables to store local state is kinda silly, it seems to me that doing a AA lookup kinda defeats the point of caching in the first place, so I want to focus on the cast method.

So:

* should we always wrap the write in a synchronized block to minimize the chances that we'll have thread problems with implicitly shared immutable things passed as const? What's the best way to do this btw?

* should objects with logical const methods deliberately not provide immutable constructors to prevent them from being immutable? Would this actually work?

* Anything else that is likely to come up?

February 15, 2014
On Saturday, February 15, 2014 04:03:50 Adam D. Ruppe wrote:
> D doesn't have logical const, but sometimes it is useful, especially with lazy initialization of a field, and we can kinda fake it with casts or with global variables. Modifying an immutable object is undefined behavior, so how can we avoid that, and if not, try to minimize the problems in practice?
> 
> Using global variables to store local state is kinda silly, it seems to me that doing a AA lookup kinda defeats the point of caching in the first place, so I want to focus on the cast method.
> 
> So:
> 
> * should we always wrap the write in a synchronized block to minimize the chances that we'll have thread problems with implicitly shared immutable things passed as const? What's the best way to do this btw?
> 
> * should objects with logical const methods deliberately not provide immutable constructors to prevent them from being immutable? Would this actually work?
> 
> * Anything else that is likely to come up?

If you want logical const, don't use const or immutable at all. To do so is undefined as they require physical constness/immutability. So, if you want logical const, you need to indicate constness in some other way that's not defined by the language. Pretty much by definition, you can't have logical const with const or immutable, because the only way to even attempt it involves casts, which means undefined behavior. I'd strongly advise against even considering it, let alone trying it.

- Jonathan M Davis
February 15, 2014
On Saturday, 15 February 2014 at 10:52:25 UTC, Jonathan M Davis wrote:
> If you want logical const, don't use const or immutable at all. To do so is
> undefined as they require physical constness/immutability. So, if you want
> logical const, you need to indicate constness in some other way that's not
> defined by the language. Pretty much by definition, you can't have logical
> const with const or immutable, because the only way to even attempt it
> involves casts, which means undefined behavior. I'd strongly advise against
> even considering it, let alone trying it.
>
> - Jonathan M Davis

+1.

Shoe-horning D's const into C++'s const is a bad idea. They are fundamentally different and shouldn't be used the same way.

I like to think D's const exists because of immutable and for that reason alone.
February 15, 2014
If you want logical const in D, you just don't use const. If you try to hack around it, it will just come back and bite you.

As a result, when writing APIs, don't enforce constness of your parameters if you require logical const. e.g. a range will not necessarily have const front and empty. Trying to enforce that will lead to trouble.
February 15, 2014
On Saturday, 15 February 2014 at 12:23:54 UTC, Peter Alexander wrote:
> If you want logical const in D, you just don't use const. If you try to hack around it, it will just come back and bite you.
>
> As a result, when writing APIs, don't enforce constness of your parameters if you require logical const. e.g. a range will not necessarily have const front and empty. Trying to enforce that will lead to trouble.

I fear it may not be that simple. Caching tends to be the main example for wanting logical const, but caching may not be the first thing implemented.

Write some code, make what you can const, build from that code, make more things const. Go back and implement caching, remove const from everything.

To me, the only requirement for implementing logical const is that you make sure it is never implemented on immutable. Casting away const is not undefined, only mutating it afterwards. But mutating a mutable address is still valid, so as long as _you_ guarantee the address is not immutable, no it won't format your harddrive.
February 15, 2014
On 02/15/2014 08:34 PM, Jesse Phillips wrote:
> But mutating a mutable address is still valid, so as long as _you_
> guarantee the address is not immutable, no it won't format your harddrive.

class C{ bool doit = true; }
void foo(const(C) c)pure{ (cast()C).doit = false; }

void main(){
    auto c = new C;
    writeln(c.doit);
    foo(c);
    if(c.doit) formatHardDrive();
}
February 15, 2014
On Saturday, 15 February 2014 at 12:23:54 UTC, Peter Alexander wrote:
> If you want logical const in D, you just don't use const. If you try to hack around it, it will just come back and bite you.
>
> As a result, when writing APIs, don't enforce constness of your parameters if you require logical const. e.g. a range will not necessarily have const front and empty. Trying to enforce that will lead to trouble.

inout is *sort of* logical const, if the underlying type is immutable.

T logicalConst(T)(inout(T) t)
{
    //Error: cannot modify inout expression t
    t += 1;
    return t;
}

void main()
{
    auto n = 0;
    auto m = logicalConst(m);
}

The only problem is figuring out if T itself is actually mutable and not immutable or const. If you can do that, then it's fine to cast inout away and mutate the value.
February 15, 2014
On Saturday, 15 February 2014 at 19:59:07 UTC, Meta wrote:
> inout is *sort of* logical const, if the underlying type is immutable.

I mean mutable, of course.

February 16, 2014
On Sat, 15 Feb 2014 15:02:44 -0500, Meta <jared771@gmail.com> wrote:

> On Saturday, 15 February 2014 at 19:59:07 UTC, Meta wrote:
>> inout is *sort of* logical const, if the underlying type is immutable.
>
> I mean mutable, of course.

No, it's not. It's not allowed to mutate with inout.

-Steve
February 16, 2014
On Sunday, 16 February 2014 at 02:34:36 UTC, Steven Schveighoffer wrote:
> On Sat, 15 Feb 2014 15:02:44 -0500, Meta <jared771@gmail.com> wrote:
>
>> On Saturday, 15 February 2014 at 19:59:07 UTC, Meta wrote:
>>> inout is *sort of* logical const, if the underlying type is immutable.
>>
>> I mean mutable, of course.
>
> No, it's not. It's not allowed to mutate with inout.
>
> -Steve

Right, but inout can accept any of mutable, const, and mutable. The compiler will statically disallow you from mutating an inout variable, but if you know the underlying data is mutable, it's safe to cast inout away and mutate. If the data is actually mutable, then inout is effectively logical const. I guess the same is true of regular const.
« First   ‹ Prev
1 2 3