Skip to content

The default for constant should be 'my' not 'our' #514

@finanalyst

Description

@finanalyst

There is a difference between Rakudo v6.d and RakuAST when compiling constant.
Consider the code (in a file tmp/numvalue-test.raku )

sub to-zh( Int() $num is copy ) {
    constant @mag = « '' 萬 億 兆 京 垓 秭 穰 溝 澗 正 載 極 恆河沙 阿僧祇 那由他 不可思議 無量大數 »;
    my $chunk-trans ~= @mag[2];
}
sub to-ja( Int() $num is copy ) {
    constant @mag = « '' 万 億 兆 京 垓 𥝱 穣 溝 澗 正 載 極 恒河沙 阿僧祇 那由他 不可思議 無量大数 »;
    my $chunk-trans ~= @mag[3];
}

Now lets compile with and without the RakuAST setting

$ raku -c tmp/numvalue-test.raku 
Syntax OK
$ RAKUDO_RAKUAST=1 raku -I. -c tmp/numvalue-test.raku 
===SORRY!===
already have an 'our constant @mag' in the package
$ raku -v
Welcome to Rakudo™ v2026.01.
Implementing the Raku® Programming Language v6.d.
Built on MoarVM version 2026.01.

Apparently, in the documentation and Roast, constant is package / our scoped. Consequently, the RakuAST parser is more correct from that point of view.

However, in an offline conversation with Damian Conway, the original design philosophy was to make everything my scoped, including constant.

Here is an extract from that conversation and quoting Damian (@thoughtstream):

Also would it be possible Damian for you to explain why the underlying design
philosophy indicates constant here should be my scoped.

Of course. The short answer is: because the underlying design philosophy
is that everything should by my scoped if possible. :-)

Our long and bitter experience is that lexical scoping is almost always safer,
more predictable, and more flexible that package scoping...

Lexical scoping is safer because package scoping creates globally accessible
entities. An our constant (or variable or subroutine) is directly accessible
everywhere in its namespace – even in other files – and also accessible
everywhere outside its namespace...provided it's fully qualified with its
package name. For constants, that universal accessibility is not quite the
disaster it is for variables, but it's still a disaster, because it potentially
couples that constant to any/every other part of the codebase. That is,
you can't know which parts of your code are relying on that constant without
examining every bit of that code (including the recompiled modular bits).
In contrast, a my constant is only accessible from the point of its declaration
to the end of its declaration block. No other section of your codebase can ever
access it, unless it is specifically passed as a parameter to that code
(which is also a lexically scoped process).

Lexical scoping is more predictable because the rules for lexical visibility are
simple and strict: "Visible from the declaration to the end of the declaration block".
Packaging scoping is unpredictable because the rules for package visibility
are even simpler but not at all strict: "Visible absolutely anywhere".
Which means you can't have two package constants with the same logical name
in the same namespace (without fully qualifying them...in which case they
no longer actually have the same name). But many algorithms, especially in
functional styles of coding, rely on being able to select names for constants
(and other entities) without needing to worry about potential name clashes.
For example, the speed of light is always a constant, but it's a constant with
different values depending on the propagation medium. And the list of ideographs
for powers-of-ten in various Asian languages is always a constant, but it's
a different constant list depending on the language. The constant's purpose
is always the same across media or language, so its name should be identical
across media- or linguistic implementations. But if a constant is our scoped
then it then becomes impossible to put two or more of those related implementations
in the same package, which makes it much harder to build them in a pure functional style.
Another way of looking at this issue is that it's easy to know what a lexical constant is,
because you just look upwards through the code until you find its declaration
in the same block (or in a surrounding block). Whereas, to know what a package constant is
you may potentially have to look everywhere in the code.

Lexical scoping is more flexible because the visibility rules for lexical entities
(such as lexical constants) allow them to be conveniently overridden within
a nested lexical scope, simply by redeclaring a new constant with the same name
in an inner block. Likewise, two separate (non-nested) blocks can declare two
versions of the same lexical constant with no concern for potential name clashes
(as my number-translation code did). Finally, lexical constants can be closed
over by closures...though, in fairness, this feature is admittedly much less compelling
and useful that closure over lexical variables or subroutines. In contrast,
all package-scoped constants are "one-and-done", making it very difficult
to use them in any of those expressive and powerful ways.

The two arguments in favour of package-scoped constants seem to be
(a) that they are easier to access outside their package scope (by fully qualifying them)
and (b) that they are in a sense "more universally constant"...in that the value
associated with their name cannot be redefined in any way within their package scope.
However, as I have suggested above, I believe that accessibility outside their scope
is actually a safety disadvantage and a predictability challenge, because
it increases code coupling. Likewise, I believe that universal constancy
is actually a flexibility limitation because real-world constants generally
don't work that way (i.e. real-world constants are almost always relative
and situational, rather than absolute and universal).

Remember too that we are only talking about the default behaviour of constants
that are declared without an explicit my or our. If we changed the official
default to my, the our behaviour would always still be available via
an explicit declaration. Except that this unsafe, unpredictable, inflexible
our behaviour would now, happily, also be explicitly marked.

Another way of looking at this issue is that a constant can be thought of as
just a parameterless subroutine that always returns the same value
(indeed, in most pure functional languages, that's the only way
to define a constant). That is:

constant pi = 3;

...is equivalent to:

sub pi () { return 3 }

Except, of course, in Raku that's not actually equivalent, because in Raku we
(rightly) make unscoped subroutines default safely to my, whereas for some reason
we're making unscoped constants default to our.

Alternatively, you might prefer to think of a constant as a variable that doesn't vary.
In Raku there are only a small number of cases where regular (i.e. untwigilled) variables
are not declared with an explicit my or our, but in all of those cases the variable
defaults to my scoping. Specifically, $_ is lexically scoped to the current block,
while $/ and $! (as well as normal parameter variables) are lexically scoped
to each subroutine. That's because, from the earliest days of designing Perl6/Raku,
we recognized that my variables were safer, more predictable, and more flexible
than package variables. We eventually extended that understanding to encompass
subroutines as well. Which is why we made lexical scoping the default for both.

A constant can be thought of as being like both of those constructs,
and hence its default should be like them too: my, not our.

Metadata

Metadata

Assignees

No one assigned

    Labels

    languageChanges to the Raku Programming Language

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions