PEP 802: Display Syntax for the Empty Set

I guess in my version of the idea there is no ‘Absence’ object. The absence sigil can’t be written outside of collection literals, function call arguments, or a match-case pattern. So params['key'] = / would be invalid syntax.

For dictionaries, both key and value get erased when the value resolves to /.

1 Like

It is certainly simpler and cleaner this way. Yet the absence of Absent (pun not intended) prevent propagating the absent value through a function return (meaning doing return Absent is impossible) thus limits scalability.

So the absent symbol could only be allowed in empty set creation, in an if-else statement, or in a match-case… (?)

I’d call it a feature if //Absent couldn’t be stored in variables or passed around as arguments. It keeps us away the should I return None or Absent dichotomy that JavaScript fell into with null vs undefined. I’d support a proposal for being able to write subprocess.run(["command", "--debug" if debug else /]) but definitely not if it meant that I’d also then have to start worrying about which placeholder a function expects or returns.

That said, if / means no value then I’d expect {/} be the same as {} and therefore become just another empty dict() literal so I’d call the / as placeholder for no value” and {/} means set() proposals incompatible rather than one being a generalisation of the other.

2 Likes

And on that note, it’d be nice if the discussion of a separate incompatible proposal went somewhere else…?

9 Likes

Essentially, this kind of discussion of/ as a syntactic pseudo-expression in an expression list is the generalization of the {/} syntax. But I don’t think / is appropriate in this case.

pass

If we’re going down that route we might as well make pass an expression which skips part of the expression list inside literals:

condition = ...
my_tuple1 = tuple(filter(None, (
    "first_item",
    "second_item" if condition else None,
    "third_item",
)))    # Current syntax requires a filter
my_tuple2 = (
    "first_item",
    "second_item" if condition else pass,
    "third_item",
)

# Type checkers will have knowledge of how many items there are statically
if condition:
    assert_type(my_tuple2, tuple[str, str, str])
else:
    assert_type(my_tuple2, tuple[str, str])

# Examples with sets and dicts
my_empty_set = {pass}    
my_empty_dict = {pass: pass}    # The pair is skipped if either key or value is pass

Outside literals, pass can only appear in an expression statement by itself, i.e. behaves the same as the pass statement now.

If such alternate proposals are developed then I don’t think OP’s PEP will be necessary.

1 Like

I think such proposals would be relevant to this thread if they existed, but they don’t exist in any serious form. If someone wants to develop such a proposal, it should go in its own thread.

4 Likes

Aesthetically it always bothered me that we have set literals but not for the empty set, so I would like to have one.

On the other hand I had gone my whole life without knowing ∅ was the mathematical symbol for an empty set, and {/} is a bit strange for Python.

It’s nice that {*()} works today, and I can work out what it means, although it’s same number of chars as set() and not visually any more concise or parseable either. Maybe it needs a catchy emoji name like ‘walrus’, ‘rocket’ etc.

3 Likes

That is θ, not ∅. :frowning:

Given there is already not enough syntax for frozenset, what is there left for frozendict ?


Have the same concern.

Thus, created `frozenset`, `frozendict` comprehensions. Consideration .

Or if TLTR, it is essentially:

Which is at worst the fallback solution if there actually is not enough syntax.

And in the best case ends up something that is actually ok, given not needing to venture into {{...}} and its various complications.

I doubt this would be the best path if all 6 were being designed at the same time from the start, but it gives me peace of mind as such fallback is not too bad.


I don’t mind {/} (voted for it) and I would be pleased if it turns out to fit well.

But given the above, I think it would be sensible not to rush with this and see what happens with frozenset and frozendict and try to manage this in parallel.

1 Like

My idea is that {/} would match a set’s grammar rule of '{' star_named_expressions '}' when / becomes a valid token for an expression, and would therefore be compiled into LOAD_CONST / and BUILD_SET 1, and BUILD_SET would discard the item when it’s found to be / at runtime (and compiler optimization can help skip LOAD_CONST /).

So no, {/} would not be treated as {}.

I disagree with the characterization of this alternative proposal being an “incompatible” one with the proposal of this thread.

The very reason I made this alternative proposal in this thread at all was to be compatible with this proposal while accommodating critiques of / being an anomoly to the language.

That said, I fully agree that further development of this alternative proposal should go into a separate thread.

2 Likes

-1, but I think for a different reason (or maybe it’s been implicit)

Long ago, teaching C was challenging in part because of the chamelon asterisk () which meant multiply except it meant “declare a pointer” except when it mean “deference a pointer.” (And ** meant power in Fortran et. al.)

/ already means divide except when it’s a path.Pathlib operator except when it’s position-only arguments in a signature. Adding another meaning just makes the human parsing of it more difficult.

To resolve the ambiguity of {}, why not just use the string convention of a prefix (e.g. b → bytes, r → raw, etc)

x = s{} # empty set
y = d{} # empty dictionary
z = {} # still means empty dictionary

Presumably, the existing “{}” could be soft-deprecated, at least in documentation.

9 Likes

jsut a couple notes after purusing the thread.

in short: I’m -0.5

I think this is a VERY strong argument for not doing this.

Ideally, the solution is to update tutorials, etc. with BOTH versions:

“if you are using Python > 3.14, you can use {/}, but older versions require set()`

ouch!

Python is way to mature and well used for this kind of churn to be worth it.

That is a good point – as a rule, I think sets are under-used, I know in my code they are (I’ve been working on that) – so anything that encourages their (appropriate) use is a good idea. However, I’m not sure that this would really help, as empty sets aren’t that common, at least in my code.

Now THAT is a PEP I could get behind (well, maybe, depends on the details, of course…) – I really like sets, but yes, let’s make them even easier to use. It’s not a big deal, but it offends my sensibilities to use a dict when I really want a more featureful set ….

Minor typo:
”We insted chose to allow whitespace between the brackets and the slash”

I understand the pain. I got on the Python train with Python3.4 and one of the first things I tried was to launch a process with the subprocess.run that was documented on docs.python.org, which was Python 3.5 at the time. (“Stupid broken language” I muttered.)

After awhile I figured out it wasn’t in 3.4 and used subprocess.check_call instead.

But that’s always going to be true – if Python adds features to the current release the default docs will mislead newbies using a prior version. It’s a one-time pain, not a reason to never expand the language or the libraries.

3 Likes

@gerardw Are you sure it didn’t say “New in version 3.5” and you just ignored that?

What is? I wasn’t proposing anything there. In fact, I am quite happy that dictionaries get all of these advantages, since they are so important to everything that Python does. Sets are far less relevant to the vast majority of Python programs, and so they benefit correspondingly less from optimizations.

sorry – very vague – I know it wasn’t a proposal, but the idea of making sets easier to use an d more function is a fine idea.

Maybe it’s just asthetics, but using dict full of worthless values rather than a set, when you really want a set (the concept, not the code) tells me that the the set api could be improved.

Spitballing here, but would making:

a_set[‘this’] = None

mean the same as:

a_set.add(‘this’)

be too stupid and confusing?

Anyway, I don’t think I’ve used a dict to express a set since I’ve known about set – so I’m not the one to come up with ideas.

Stupid and confusing? Nope, in fact, that’s very close to how Pike’s sets work. You assign a truthy value to add to the set, assign false to remove. I think that would be a perfectly fine approach to take.

1 Like

Of course it said that. I didn’t know you had to scroll down to find the gotchas.

1 Like

Yes, it makes a set look like a dictionary.

That’s unfair. You have two perfectly good options: either choose the documentation for your version, or check the “version added” on things. Or always run the latest alpha… three options. AMONG OUR OPTIONS ARE choosing the documentation for your version, checking the version added, and running the latest alpha… and testing things in the REPL before you run the code. Err, I’ll come in again.

3 Likes

Ah yes – that’s better:

a_set[‘this’] = True is a_set.add(‘this’)

a_set[‘this’] = False is a_set.remove(‘this’)

nice!

and:

del a_set[‘this’] is a_set.remove(‘this’)

Not that I’m going to write the PEP.

I guess the question is – are there a lot of people that want the dict-like API enough that they use dict when set would do?

For me – I like the idea, but I still choose to use set when that’s the functionality I need, even though the API might not be ideal.

1 Like