On Mon, 16 Nov 2020 08:39:30 +1100
Steven D'Aprano <st...@pearwood.info> wrote:


> > The baseline of my version is much simpler:
> > 
> > # This makes "const" a kind of hard keyword for this module
> > from __future__ import const
> > 
> > FOO: const = 1  # obviously, this is constant

> Oh, well, 

To start with, in the original thread I wanted to concentrate on issues
of the PEP634/PEP635, and whether these issues are prominent enough to
hope for them being addressed. So, I changed the subject (and we'd soon
be excused to python-ideas, indeed, I cc: there).

> if all it takes is to add a new keyword, then constants are 
> easy! 

A new annotation. And the new subject is "constants in Python: Starting
simple and gradually adding more", which hopefully should set the
theme. Please remember how we arrived here: it's from the fact that
PEP634 doesn't allow for the following trivial code:

match foo:
    case MY_CONST:

We're considering (another alternative) how to address that. Please
don't make a leap towards supporting (at once) const-ness equivalent to
statically typed languages.

> No need to worry about how constantness affects name resolution,
> or how the syntax interacts with type-hints:

"const" is *the* type hint.

>     spam: const: Widget = build_widget(*args)

That syntax is clearly invalid. And composability of type annotations
(aka type hints) is a known, looming issue. We now have
https://www.python.org/dev/peps/pep-0593/ , but in all fairness, it
seems like a stopgap measure rather than an elegant solution. (In full
fairness, entire "typing" module's annotations aren't very elegant,
but as we know, it's not a part of language core, but part of
CPython's stdlib. That means it's only *one* of possible annotation
schemes for *Python*).

So, under PEP593, the above would be written

    spam: Annotated[Widget, const]  = build_widget(*args)

If you want a glimpse of what alternatives might look, then: given that
"|" is going to be used for union types, why not try "&" for

    spam: Widget & const  = build_widget(*args)

But again, for "initial support of constants in Python, prompted by the
introduction of pattern matching facilities", we don't need to worry
about all that right away.

>     # Maybe this is better?
>     # let spam: Widget = build_widget(*args)

As you remember, at the beginning of my proposal, I wrote "The baseline
of my version ...". Baseline == level 0. For "level 2", I considered

const spam: Widget = ...

I don't think that "let" should be used. We're putting emphasis on
*constantness* (not lexical scoping of variables [immutable by
functional tradition, though that's "minor"]). JavaScript (now) has
both "const" and "let", and I think that's pretty reasonable approach.
So, let's save "let" for possible later uses.

So, what's "level 1"?

As I mentioned, under "level 0", "from __future__ import const" has a
magic meaning (akin to other "from __future__"'s). Under "level 1",
"const" is just a normal annotation symbol imported from a module. So,
following would be possible:

from __future__ import const

FOO: const = 1

def fun():
    const = 1  # Just a variable in function scope

    def sub():
        # Gimme powerz back
        from __future__ import const
        BAR: const = 2

# Back to annotation in global scope
BAZ: const = 3

"level 0" should be implementable in CPython in ~1hr.
"level 1" is realistically what we should shoot for.
"level 2" (the dedicated keyword), I'm still not sure about. "const" is
very baseline annotation, and deserves a dedicated keyword. But the
underlying problem is composability of (arbitrary) annotations. I'd
rather keep searching for graal in that regard, before giving up and
introduce a dedicated thing just for "const". 

> or how "constant" interacts with mutability:
>     spam: const = []
>     spam.append(99)  # Allowed?
>     spam.extend(items)
>     spam.sort()

"const" is an annotation just like any other. And it affects *runtime*
in the same as any other annotation in CPython affects it: in no way.

"const" is however introduced as a hint for *compile-time*, so compiler
could make some simple inferences and maybe even optimizations based
on it.

> or for that matter, whether or not constants are actually constant.
>     spam: const = 1
>     spam = 2

Compiler can, and thus would, catch that as an error (warning for a
beta version?).

> If constants aren't actually constant, but just a tag on symbols,
> then you would be right, it probably would be trivially easy to add 
> "constants" to Python.


> But I think most people agree making them behave as constants is a 
> pretty important feature.

As mentioned, "const" is just an annotation like any other, except
compiler has some insight into it. Dealing with runtime is distant goal
for CPython. (Which is for now offloaded to static type checkers and
libraries/alternative Python implementations.)

> *The* most critical feature of all, really. 
> Given a declared constant:
>     # Module a.py
>     x: const = None
> how do you prevent not just code in module `a` from rebinding the
> value:

Outside of the current scope of the discussion.

However, if you're interested in that topic, then I've implemented it in
my Python dialect, Pycopy (https://github.com/pfalcon/pycopy). It's on
my TODO to post an RFC to python-ideas for beating.


Best regards,
 Paul                          mailto:pmis...@gmail.com
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
Message archived at 
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to