Discussion:
Global const variables
bearophile via Digitalmars-d-learn
2014-10-21 08:02:50 UTC
Permalink
Currently this code gets rejected:

const int[] a = [1];
void main() pure {
auto y = a[0];
}


test2.d(3,14): Error: pure function 'D main' cannot access
mutable static data 'a'
test2.d(3,14): Error: pure function 'D main' cannot access
mutable static data 'a'

But is this a good idea? Isn't it better to accept it?

Bye,
bearophile
Minas Mina via Digitalmars-d-learn
2014-10-21 08:22:09 UTC
Permalink
Post by bearophile via Digitalmars-d-learn
const int[] a = [1];
void main() pure {
auto y = a[0];
}
test2.d(3,14): Error: pure function 'D main' cannot access
mutable static data 'a'
test2.d(3,14): Error: pure function 'D main' cannot access
mutable static data 'a'
But is this a good idea? Isn't it better to accept it?
Bye,
bearophile
Aren't pure functions supposed to return the same result every
time? If yes, it is correct to not accept it.
bearophile via Digitalmars-d-learn
2014-10-21 08:25:07 UTC
Permalink
Post by Minas Mina via Digitalmars-d-learn
Aren't pure functions supposed to return the same result every
time? If yes, it is correct to not accept it.
But how can main() not be pure? Or, how can't the 'a' array be
immutable?

Bye,
bearophile
safety0ff via Digitalmars-d-learn
2014-10-21 08:48:07 UTC
Permalink
Post by bearophile via Digitalmars-d-learn
Post by Minas Mina via Digitalmars-d-learn
Aren't pure functions supposed to return the same result every
time? If yes, it is correct to not accept it.
But how can main() not be pure? Or, how can't the 'a' array be
immutable?
Bye,
bearophile
There can exist a mutable reference to a's underlying memory:

const int[] a;
int[] b;

static this()
{
b = [1];
a = b;
}
Szymon Gatner via Digitalmars-d-learn
2014-10-21 09:22:21 UTC
Permalink
Post by safety0ff via Digitalmars-d-learn
Post by bearophile via Digitalmars-d-learn
Post by Minas Mina via Digitalmars-d-learn
Aren't pure functions supposed to return the same result
every time? If yes, it is correct to not accept it.
But how can main() not be pure? Or, how can't the 'a' array be
immutable?
Bye,
bearophile
const int[] a;
int[] b;
static this()
{
b = [1];
a = b;
}
Ant this code works? What is the point of const then if you can
assign it to mutable slice?
bearophile via Digitalmars-d-learn
2014-10-21 11:02:15 UTC
Permalink
Post by Szymon Gatner via Digitalmars-d-learn
Post by safety0ff via Digitalmars-d-learn
const int[] a;
int[] b;
static this()
{
b = [1];
a = b;
}
Ant this code works? What is the point of const then if you can
assign it to mutable slice?
It works, and I think it should work. Inside the (module)
constructor the const state is handled differently.

Thank you for the example, safety0ff.

Bye,
bearophile
Solomon E via Digitalmars-d-learn
2014-10-21 12:08:33 UTC
Permalink
Post by safety0ff via Digitalmars-d-learn
Post by bearophile via Digitalmars-d-learn
Post by Minas Mina via Digitalmars-d-learn
Aren't pure functions supposed to return the same result
every time? If yes, it is correct to not accept it.
But how can main() not be pure? Or, how can't the 'a' array be
immutable?
Bye,
bearophile
const int[] a;
int[] b;
static this()
{
b = [1];
a = b;
}
`a` isn't a reference to `b`. `a` is assigned by value and has
its own storage. You could change its type to const int[]* a =
&b; then it would be a reference to mutable storage. I made an
example program to figure these things out, or else I wouldn't
know what I'm talking about.

import std.stdio;
import std.conv;

const int[] a;
int[] b;

static this()
{
string entry;
while(entry == "") {
try {
write("enter an int: ");
entry = readln();
b = [to!int(entry[0..entry.length-1])];
} catch(ConvException e) {
writeln("error, try again");
entry = "";
}
}
a = b;
}

int[] x = [0,1,2,3];

class Holder
{
const(int[]) y;
this() { y = x; }
}

void main()
{
auto H = new Holder();
writeln("original const a ", a); // [the int that was entered]
b = [8,7];
writeln("unaltered const a ", a); // [the int that was
entered]
x = [10,9];
writeln("unaltered const member y ", H.y); // [0, 1, 2, 3]
H = new Holder();
writeln("new const member y ", H.y); // [10, 9]
writeln("immutable m ", get_m()); // [42]
}

immutable int[] m = [42];

immutable(int[]) get_m() pure
{
return m;
}
anonymous via Digitalmars-d-learn
2014-10-21 12:30:30 UTC
Permalink
Post by Solomon E via Digitalmars-d-learn
Post by safety0ff via Digitalmars-d-learn
const int[] a;
int[] b;
static this()
{
b = [1];
a = b;
}
`a` isn't a reference to `b`. `a` is assigned by value and has
its own storage.
`a` is indeed a copy of `b`. But `b` is a pointer+length, and
only those are copied. The array data is not copied. `a` and `b`
refer to the same data afterwards.

[...]
Post by Solomon E via Digitalmars-d-learn
const int[] a;
int[] b;
static this()
{
[...]
Post by Solomon E via Digitalmars-d-learn
a = b;
}
[...]
Post by Solomon E via Digitalmars-d-learn
void main()
{
[...]
Post by Solomon E via Digitalmars-d-learn
b = [8,7];
Here, making `b` point somewhere else (to [8, 7]). If instead you
change b's elements, you'll see that `a` and `b` refer to the
same data:

b[] = 8; /* Will also change `a`'s data. */
Solomon E via Digitalmars-d-learn
2014-10-21 13:43:29 UTC
Permalink
Post by anonymous via Digitalmars-d-learn
Post by Solomon E via Digitalmars-d-learn
Post by safety0ff via Digitalmars-d-learn
const int[] a;
int[] b;
static this()
{
b = [1];
a = b;
}
`a` isn't a reference to `b`. `a` is assigned by value and has
its own storage.
`a` is indeed a copy of `b`. But `b` is a pointer+length, and
only those are copied. The array data is not copied. `a` and `b`
refer to the same data afterwards.
[...]
Post by Solomon E via Digitalmars-d-learn
const int[] a;
int[] b;
static this()
{
[...]
Post by Solomon E via Digitalmars-d-learn
a = b;
}
[...]
Post by Solomon E via Digitalmars-d-learn
void main()
{
[...]
Post by Solomon E via Digitalmars-d-learn
b = [8,7];
Here, making `b` point somewhere else (to [8, 7]). If instead
you
change b's elements, you'll see that `a` and `b` refer to the
b[] = 8; /* Will also change `a`'s data. */
You're right. Thank you, anonymous stranger.

Sorry about that, safety0ff. It looks like you were right and I
was wrong.

`b[0] = 8;` or `b[] = 8;` changes a. Printing the values for &a
and &b shows they're different pointers, but (a is b) returns
true. So I still have more to learn about how it does that.
ketmar via Digitalmars-d-learn
2014-10-21 14:25:09 UTC
Permalink
On Tue, 21 Oct 2014 13:43:29 +0000
Post by Solomon E via Digitalmars-d-learn
`b[0] = 8;` or `b[] = 8;` changes a. Printing the values for &a
and &b shows they're different pointers, but (a is b) returns
true. So I still have more to learn about how it does that.
that's 'cause '&b' taking address of hidden "array structure", not
the first array element, as in C. try 'a.ptr' and 'b.ptr' to get
addresses of array elements.
ketmar via Digitalmars-d-learn
2014-10-21 14:27:50 UTC
Permalink
On Tue, 21 Oct 2014 17:25:09 +0300
Post by ketmar via Digitalmars-d-learn
On Tue, 21 Oct 2014 13:43:29 +0000
Post by Solomon E via Digitalmars-d-learn
`b[0] = 8;` or `b[] = 8;` changes a. Printing the values for &a
and &b shows they're different pointers, but (a is b) returns
true. So I still have more to learn about how it does that.
that's 'cause '&b' taking address of hidden "array structure", not
the first array element, as in C. try 'a.ptr' and 'b.ptr' to get
addresses of array elements.
p.s. '&(a[0])' and '&(b[0])' works too. parens are just for clarifying.
Solomon E via Digitalmars-d-learn
2014-10-21 16:47:04 UTC
Permalink
On Tuesday, 21 October 2014 at 14:25:20 UTC, ketmar via
Post by ketmar via Digitalmars-d-learn
On Tue, 21 Oct 2014 13:43:29 +0000
Solomon E via Digitalmars-d-learn
Post by Solomon E via Digitalmars-d-learn
`b[0] = 8;` or `b[] = 8;` changes a. Printing the values for
&a and &b shows they're different pointers, but (a is b)
returns true. So I still have more to learn about how it does
that.
that's 'cause '&b' taking address of hidden "array structure",
not
the first array element, as in C. try 'a.ptr' and 'b.ptr' to get
addresses of array elements.
Thanks, that's what I was looking for, in order to understand
what's going on. I Googled for it on this site, but without
remembering the keyword ptr, I didn't find anything relevant.

After I put printouts of .ptr in my test program, I figured out
how to get the same result by unsafe pointer arithmetic.
Apparently, for an array a, a.ptr == *(cast(ulong*) &a + 1).
That's unsafe because the implementation might change, and
pointer arithmetic is unsafe in general.
ketmar via Digitalmars-d-learn
2014-10-21 16:56:53 UTC
Permalink
On Tue, 21 Oct 2014 16:47:04 +0000
Post by Solomon E via Digitalmars-d-learn
That's unsafe because the implementation might change, and
pointer arithmetic is unsafe in general.
sure, ponter casting is implementation-dependend. but .ptr is
guaranteed to work as expected. 'a' is just a `struct { size_t length;
void* ptr }` now, but this representation is implementation detail, not
a convention.
MachineCode via Digitalmars-d-learn
2014-10-21 15:51:24 UTC
Permalink
Post by bearophile via Digitalmars-d-learn
const int[] a = [1];
void main() pure {
auto y = a[0];
}
test2.d(3,14): Error: pure function 'D main' cannot access
mutable static data 'a'
test2.d(3,14): Error: pure function 'D main' cannot access
mutable static data 'a'
But is this a good idea? Isn't it better to accept it?
Bye,
bearophile
pure functions are also supposed to don't use global variables at
all, according to functional programming paradigm
Solomon E via Digitalmars-d-learn
2014-10-21 16:56:05 UTC
Permalink
On Tuesday, 21 October 2014 at 15:51:27 UTC, MachineCode wrote:
...
...
Post by MachineCode via Digitalmars-d-learn
pure functions are also supposed to don't use global variables
at all, according to functional programming paradigm
Pure functions are immutables (constants but not "const" in the D
or C++ senses) and can use other immutables, even if they're
global immutables. (I guess it would be better though always to
have a named top scope instead of everyone in the world having
the same global scope :-)
Meta via Digitalmars-d-learn
2014-10-21 17:00:48 UTC
Permalink
Post by Solomon E via Digitalmars-d-learn
...
...
Post by MachineCode via Digitalmars-d-learn
pure functions are also supposed to don't use global variables
at all, according to functional programming paradigm
Pure functions are immutables (constants but not "const" in the
D or C++ senses) and can use other immutables, even if they're
global immutables. (I guess it would be better though always to
have a named top scope instead of everyone in the world having
the same global scope :-)
You *do* have a named top scope, the module name (which is
accessible with prefixing a symbol with ., i.e., .x refers to the
symbol x at module scope). There is no such thing as global scope
in D.
Jonathan M Davis via Digitalmars-d-learn
2014-10-21 17:12:25 UTC
Permalink
There is no such thing as global scope in D.
While that's technically true (and very good for avoiding symbol
conflicts), modules at the module level are still typically
referred to as global variables.

Jonathan M Davis via Digitalmars-d-learn
2014-10-21 17:09:57 UTC
Permalink
Post by MachineCode via Digitalmars-d-learn
pure functions are also supposed to don't use global variables
at all, according to functional programming paradigm
The functional programming paradigm is kind of irrelevant to D's
pure, which should really be something more like @global. D's
pure makes it so that a function cannot directly access global,
mutable state - i.e. no mutable global or static variables which
can ever be mutated by anything in the program. So, pure
functions can access immutable global and static variables,
because their state can never change, and in principle, they
could access const variables that were directly initialized, e.g.

const int i = 7;

However, apparently, the compiler won't do that with arrays right
now, as Bearophile has found. Accessing global or static
variables that can never change once they're initialized does not
violate the guarantees that D's pure makes, because the value is
fixed and as such is essentially the same as hard-coding the
value in the function directly. It's just those that can change
which are a problem (which does potentially include global, const
arrays if they were initialized via a static constructor).

Now, while D's pure really doesn't directly have anything to do
with functional purity (_all_ it does is restrict access to
global or static variables which can be mutated - either directly
or indirectly), it _is_ a vital building block for functional
purity, because if the function parameters are immutable or
implicitly convertible to immutable, then the compiler knows that
multiple calls to the function with the same arguments will
always return the same result, because the function can't access
any mutable globals to get at anything different to produce a
different result. And even if the parameters aren't immutable or
implicitly convertible to immutable, if the parameter types and
return type are unrelated, the compiler can also know that the
return value was not passed into the function (since it had
nowhere else to get it from), so it knows that it's unique and
can do stuff like implicitly convert that value to immutable
safely.

So, with pure, the compiler can recognize actual, functional
purity and other useful attributes and take advantage of them,
but you're probably better off if you don't think of D's pure as
being functionally pure, because there are quite a few things
that D's pure functions can do which functionally pure functions
can't do (like mutate their arguments if they're mutable).

A good article on D's pure:
http://klickverbot.at/blog/2012/05/purity-in-d/
Jonathan M Davis via Digitalmars-d-learn
2014-10-21 16:52:12 UTC
Permalink
On Tuesday, October 21, 2014 08:02:50 bearophile via
Digitalmars-d-learn
Post by bearophile via Digitalmars-d-learn
const int[] a = [1];
void main() pure {
auto y = a[0];
}
test2.d(3,14): Error: pure function 'D main' cannot access
mutable static data 'a'
test2.d(3,14): Error: pure function 'D main' cannot access
mutable static data 'a'
But is this a good idea? Isn't it better to accept it?
In principle, it should be fine, but because it's using const, it
won't work.
global or static variables which are directly initialized with
values that
cannot possibly have mutable references elsewhere in the code are
the only
case where accessing const variables from outside a pure function
like this
could work - i.e. the cases where immutable and const are
essentially
identical (the only real difference being that if the variable is
a reference
type, if it's immutable, it's also shared, whereas if it's const,
it's
thread-local). So, I don't think that it's at all surprising that
the compiler
rejects it. It should probably be made smarter so that it doesn't
reject it,
but because you can just as easily make the variable immutable,
you're not
losing any real functionality in the interim.

- Jonathan M Davis
Continue reading on narkive:
Loading...