Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

> In D, `immutable(int)* x` cannot be changed by any reference to the value.

even if I use a magnetic needle to change the bits in my RAM ? :)



sort by: page size:

> Since Go doesn't have a concept of immutable data types (except consts)

I believe this is incorrect. Strings are immutable, for example. You could also say the same about pointers and maybe a few other data types.


> Just because it is an immutable value doesn’t mean it needs to live on the stack.

Right. But the context of the example made me imagine it probably was -- since I would likely reject out-of-hand any new language that always allocated variables on the heap.


> Yet you can absolutely write: > String str = "hello,"; > str += "world!"; So it's "immutable" for some lawyerish definition which does me no good.

`str` is a mutable reference to an immutable object. You can make the reference point to something else, but the object being pointed to is immutable, so eg if you pass it to a function, that function cannot change it under you. If you want to make the reference immutable, declare it as final. I don't think understanding the difference between a pointer and the value it points to is lawyerish.


> > It's only violating your idiosyncratic insistence that there can only be one in-memory representation of any immutable value.

> I never said this!

The standards you have set require either

(1) That there is only one in-memory representation of a given value, or

(2) The language has no facilities that allow you to inquire about the in-memory representation used by a particular reference to a value.


> But it's clearly not what people mean when they talk about pointers (or references) being mutable.

The semantics of a programming language is what it is, not what you want it to be, unless you explicitly choose a language that allows you to express exactly what you mean.


> But nobody does that. Chances are someone on the team takes a shit about unnecessary memory copies

It’s not just performance. In any moderately complicated C++ program, you will want a function to mutate its argument, and this falls apart.

Sure, you can write in a pure functional style with immutable data structures, but I wish you luck implementing an immutable data structure with asymptotically reasonable performance without using pointers of some sort.


>If you understand how computers work it is extremely reliable bar none.

On my processor, INT64_MAX + 1 will be INT64_MIN, a negative value. But the compiler is free to turn

  for (int64_t i = 1; i > 0; i++) {
  	f(i);
  }
into an infinite loop. Understanding how the computer works without reading about undefined behavior will make you fall into these kinds of traps.

> But you can't do pointer arithmetics in Nim.

Actually you can, by using casts.


> The immutable->mutable trick seems to be caputred by the type checker.

Just add the immutable variable's reference to a mutable array, now mutate the immutable variable using array[0], magic!!

The compiler is so basic and naive, it's laughable how their authors have guts to fight everyone about it's stability.


> The immutability is already implied by the 'let' and 'var'.

The immutability of the binding and that of the value are different concerns. let and var are about the binding's mutability, trying to munge them together only ends up in tears.

> The proposal is similar to proposing the following declaration in C:

No. The proposal is similar to the following C:

    int const * i;

> That's why D has an `immutable` qualifier, giving the optimizer to do optimizations assuming it does not change.

Which optimizations are enabled by immutable data? I can think of constant folding. Is the data statically allocated in a read-only page?

> For a famous example, Fortran assumes two arrays never overlap. In C/C++ they can. This is the source of a persistent gap in performance between Fortran and C/C++.

What sort of optimizations does this assumption enable? Does it allow the compiler to freely reorder or parallelize the code?

C assumes that pointers to different types are never equal. This is incompatible with common systems programming concepts such as type punning. Even something simple like reinterpreting some data structure as an array of uint8_t can make the optimizer introduce bugs into the code. Notably, the Linux kernel is compiled with strict aliasing disabled:

https://lkml.org/lkml/2003/2/26/158

https://lkml.org/lkml/2009/1/12/369


> That is integer arithmetic, not pointer arithmetic.

Yeah, but I did say: "which operation could any C compiler disallow in practice: converting from pointer to integer, arithmetic on integers, or converting from integer to pointer?"

If C/C++ compilers keep breaking pointer arithmetic in the game of exploiting undefined behavior for optimizations, people are going to start doing pointer arithmetic with integers when they need it. And they do need it sometimes, for debuggers, profilers, memory checkers, JITs, garbage collectors, shared memory, and so on.


> Technically, int32_t is bad in that way, but we are not fooled by it.

Meaning? On my system int32_t is directly typedefed to unsigned int, absolutely no hidden pointers or conversions.


> Personally, I love arbitrary memory access.

C++ does not really permit this. It is incredibly hard to do type punning from void* without hitting UB. You really are only allowed to access objects through their names according to their lifetimes.

Here (https://www.youtube.com/watch?v=IAdLwUXRUvg) is a fun talk that involves trying to do a pretty basic kind of arbitrary memory access and concluding that it is simply not possible in C++.


> the same people who think that it's okay that memcpy(0, 0, 0) is undefined behavior.

I am curious about this particular example. How does that ever make sense, where do you have that?

I'd accept that it would be nice to have memcpy(p1, p2, n) with valid pointers p1 and p2 to still be defined when n=0, because maybe n is variable and might work out to be 0 in some cases, which you then don't need to treat specially. I don't know whether that case is defined or not.

But in the case where either p1 or p2 are NULL, the memcpy will be illegal no matter what n is, so if p1 or p2 are variable you always have to check them anyway, and to run into the memcpy(0, 0, 0) case you would actually have to special case n=0 when p1 or p2 were NULL, which makes no sense.

Unless 0 is actually a valid address (kernel code with identity mapping at 0, simple platforms), but then NULL becomes a whole different beast.


> Your proposal replaces my use of an array with two things, a pointer (as before) and a length. This is not too helpful, because I already could have done that if I'd wanted to.

C doesn't have a reasonable way of doing that. I know my proposal works, because we've been using it in D for 20 years.


> I would say that that transformation is legal, it doesn't even involve pointers.

you don't know that void b() isn't implemented as

    void b() {
       int* ptr = make_a_valid_pointer_from_an_integer(1638541351);
       *ptr = 10;       
    }
with 1638541351 sometimes being the address of x above ?

> You can cast T to const T, but not vice versa.

Humm, actually, you can. But guess what ? Modification of the underlying data is an UB !


> This is pretty basic C++/computer architecture stuff.

The C rules for integer type promotions are incredibly hard to reason about. They should have just banned implicit conversions from the beginning. This is not super basic stuff.

next

Legal | privacy