[Python-ideas] User-defined literals
Steven D'Aprano
steve at pearwood.info
Thu Jun 4 14:08:36 CEST 2015
On Wed, Jun 03, 2015 at 12:43:00PM -0700, Andrew Barnert wrote:
> On Jun 2, 2015, at 19:52, Steven D'Aprano <steve at pearwood.info> wrote:
[...]
> > But, really, your proposal is in no way, shape or form syntax for
> > *literals*,
>
> It's a syntax for things that are somewhat like `2`, more like `-2`,
> even more like `(2,)`, but still not exactly the same as even that.
Not really. It's a syntax for something that is not very close to *any*
of those examples. Unlike all of those example, it is a syntax for
calling a function at runtime.
Let's take (-2, 1+3j) as an example. As you point out in another post,
Python may constant-fold it, but isn't required to. Python 3.3 compiles
it to a single constant:
LOAD_CONST 6 ((-2, (1+3j)))
but Python 1.5 compiles it to a series of byte-code operations:
LOAD_CONST 0 (2)
UNARY_NEGATIVE
LOAD_CONST 1 (1)
LOAD_CONST 2 (3j)
BINARY_ADD
BUILD_TUPLE 2
But that's just implementation detail. Whether Python 3.3 or 1.5, both
expressions have something in common: the *operation* is immutable (I
don't mean the object itself); there is nothing you can do, from pure
python code, to make the literal (-2, 1+3j) something other than a
two-tuple consisting of -2 and 1+3j. You can shadow int, complex and
tuple, and it won't make a lick of difference. For lack of a better
term, I'm going to call this a "static operation" (as opposed to dynamic
operations like called len(x), which can be shadowed or monkey-patched).
I don't wish to debate the definition of "literal", as that may be very
difficult. For example, is 2+3j actually a literal, or an expression
containing only literals? If a literal, how about 2*3**4/5 for that
matter? As soon as Python compilers start doing compile-time constant
folding, the boundary between literals and constant expressions becomes
fuzzy. But that boundary is actually not very interesting. What is
interesting is that every literal shares at least the property that I
refer to above, that you cannot redefine the result of that literal at
runtime by shadowing or monkey-patching.
Coming from that perspective, a literal *defined* at runtime as you
suggest is a contradiction in terms. I don't care so much if the actual
operation that evaluates the literal happens at runtime, so long as it
is static in the above sense. If it's dynamic, then it's not a literal,
it's just a function call with ugly syntax.
> If
> you don't like using the word "literal" for that, you can come up with
> a different word. I called it a "literal" because "user-defined
> literals" is what people were asking for when they asked for `2.3d`,
If you asked for a turkey and cheese sandwich on rye bread, and I said
"Well, I haven't got any turkey, or rye, but I can give you a slice of
cheese on white bread and we'll just call it a turkey and cheese rye
sandwich", you probably wouldn't be impressed :-)
> A literal is a notation for expressing some value that means what it
> says in a sufficiently simple way.
I don't think that works. "Sufficiently simple" is a problematic
concept. If "123_d" is sufficiently simply, surely "d(123)" is equally
simple? It's only one character more, and it's a much more familiar
and conventional syntax.
Especially since *_d ends up calling a function, which might as well be
called d(). And if it is called d, why not a more_meaningful_name()
instead? I would hope that the length of the function name is not the
defining characteristic of "sufficiently simple"? (Consider
123_somereallylongbutmeaningfulnamehere.)
I don't wish to argue about other languages, but I think for Python, the
important characteristic of "literals" is that they are static, as
above, not "simple". An expression with nested containers isn't
necessarily simple:
{0: [1, 2, {3, 4, (5, 6)}]} # add arbitrary levels of complexity
nor is it necessarily constructed as a compile-time constant, but it is
static in the above sense.
[...]
> > Otherwise, we might as well say that
> >
> > from fractions import Fraction
> > Fraction(2)
> >
> > is a literal, in which case I can say your proposal is unnecessary as we
> > already have user-specified literals in Python.
>
> In C++, a constructor expression like Fraction(2) may be evaluable at
> compile time, and may evaluate to something that's constant at both
> compile time and runtime, and yet it's still not a literal. Why?
> Because their rule for what counts as "sufficiently simple" includes
> constexpr postfix user-literal operators, but not constexpr function
> or constructor calls.
What is the logic for that rule? If it is just an arbitrary decision
that "literals cannot include parentheses" then I equally arbitrarily
dismiss that rule and say "of course they can, the C++ standard not
withstanding, and the fact that Fraction(2) is a constant evaluated at
compile time is proof of that fact".
In any case, this is Python, and arguing over definitions from C++ is
not productive. Our understanding of what makes a literal can be
informed by other languages, but cannot be defined by other languages --
if for no other reason that other languages may not all agree on what
is and isn't a literal.
--
Steve
More information about the Python-ideas
mailing list