There is syntax for an empty frozenset - frozenset() - just like there is for set - set(). It’s not a compile time syntax, but who cares?
I agree that we don’t need syntax for an empty set or frozenset. But I think it’s sufficient to argue against this proposal on its own, without confusing the issue by introducing frozensets.
Well, I don’t think it does improve readability, but even if it did, it would only do so because there’s a syntax for sets. So the argument doesn’t apply to frozensets. (It would in the future, when someone proposes a frozenset syntax, but that’s not relevant for now).
I hope no-one is really arguing that this improves performance. Creating an empty set is (in my experience) basically always an initialisation. And you don’t initialise in a hot loop, so you shouldn’t be creating empty sets in a hot loop. So the performance benefit of a literal is essentially irrelevant
Actually, this makes me question whether an empty frozenset is even worth having. Given that you can’t add to a frozenset, why would you even create an empty one - and even more so, why would you create more than one?
Sets (not frozensets) are mutable, meaning that using them as a default for some parameter is dumb. Frozensets are also not used in a lot of cases where we would need to have the syntax be short. But I agree that making a syntax for sets makes the need for special frozenset syntax grow too. Using the prefix idea I explained here
we could add some fs prefix for frozensets (and fd for frozendicts). The only use case I could think of, where an empty frozenset is used would be some function when expect to return one.
Here we can see the usage of empty frozenset constructors, and same could be useful for empty frozendicts. The idea of fs and fd stems from here by the way:
Please don’t overblow the issue. Mutable defaults are not “dumb”. They have specific semantics that are not, in some situations, what the programmer wanted; but there are other situations where that’s exactly correct.
I think these are literally orthogonal, and therefore independent and unrelated.
If we have a symbol for “empty-set-ness” (be it slash, or minus, or dot, or an empty trash bin icon) and a different “frozen-ness” symbol (be it f prefix, or double braces, or an iceberg icon postfix), they can be combined in any way (and also create the currently uninhabited void for frozenmap) like below (I chose slash and f for typeability):
I just skimmed the 107 posts on this topic. I’m -1 as I don’t think it adds enough to readability to warrant the change. I also think any perf win is a red herring as you’re worrying about such a small cost that if that’s your concern I would suggest moving to Rust for your coding needs. The only win I saw was for ast.literal_eval(), but honestly I don’t think you’re going to need the empty set that often to make me up my vote.
I honestly would rather go all Fortress-like and support ⌀ , and even then it would be best as a __future__ easter egg.
I think a worthwhile outcome of this proposal would be that CPython optimizes set(), like a few folks described (and tested) as being possible with minimal changes.
Depends what you mean by worthwhile, I guess. Every line adds up.
If somebody shows me real-world code that’s seriously impacted by empty set call overhead in a hot loop, then sure, we can probably go ahead and add it to the JIT now. But more likely, 90% of the benefit will just naturally fall out of work we already have planned for optimizing builtin type calls anyways. I personally think optimizing a call like set(thing) is more interesting/important than the zero-argument case, but maybe that’s just me.
One needs to create an empty set in a hot loop because there’s no support for deferred evaluation of an argument from some of the core functions:
And also when resetting a series of sets:
And when the initialization of a new set is done for each iteration of a hot loop:
Testing if any of a series of sets is empty is supposed to be efficient when written in an expressive manner by comparing them to an empty frozenset, but is currently burdened with the unnecessary overhead of repeated object creation at runtime:
I haven’t thought particularly hard about the implications of this, so please take my input with a grain of salt, but just on a personal level I would be thrilled to see this syntax. It’s interesting to me that Guido did not think he’d ever guess {/} for the empty set, because for me, a few weeks after I started using set literal syntax, I needed to have an empty set and I thought “Oh I guess I can’t use {}, that’s a dict, but… wait, do you just put a slash here, like a comma with a tuple?” and was almost surprised that {/}didn’t work when I tried it. So this syntax is extremely intuitive as far as I’m concerned!
For a new sentinel to solve this problem, it should have the property of being non-collectible within sets and possibly lists (If it is non-colllectible within tuples, it creates unpredictability of the number of returned values from functions). But it should exist as a value itself, assignable to other variables. Thus it would be more of a “non-collectible” sentinel.
Naming it NonC thereafter, it allows not only for the creation of an empty set with {NonC} but also for a simpler(?) creation of conditional collection literals, for example : [1 if cond else NonC, 2] would be [1, 2] if cond were True else [2].
It would be hard to teach why the sentinel only works in only certain types of collections but not the others if it is not generally applicable. I don’t have a good solution to the possible unpredictability of the number of returned values from functions other than to document the best practice when opting in support for the sentinel.
NonC is plain unreadable to me. Conditionally including items when building a list is exactly one of the use cases of the Absence/Absent// sentinel suggested in my post.
I dont get what problem this is intended to solve. A empty set is a empty set. Adding some indicator that it is empty is kinda illogical and sounds like someone justifying their job. Having a character inside a set to indicate its empty could cause issues if a person wanted to use that character. The argument about language is also misguided and the fact that you are making it easier for begineers is just bad ideating i believe. Linus rejects code changes to the linux kernal to address buggy hardware and we should reject code changes due to buggy developers. The near entire writup for this change yells armchair developers in my opinion and I am peer-reviewed published for computational science and also a senior software engineer…
Yes, if we had / stand for a symbol of general absence then not only would {/} make sense but we can have some interesting conditional inclusion syntax:
Just for the record, {/} is slightly annoying to type on a French AZERTY keyboard (need to switch back-and-forth between AltGr-modified and Shift-modified keys), while set() is not.
Here are 2 possibilities, the ‘orange’ item can be automatically deleted or assigned an Absent value… then params.values() would return [apple] anyway. (In the deletion case, would params[‘key’] = / be an alternative to del params[’key’] ? In the other option, len(params.keys()) will not equal len(params.values())
Nice idea with the match statement, which probably need more details though.