Here’s a cleaned up diff:
diff --git "a/pep-0795" "b/pep-0795.rst"
index 12b6ffe..26aa4ec 100644
--- "a/pep-0795.rst"
+++ "b/pep-0795.rst"
@@ -26,8 +26,6 @@ but user-facing functions are delivered in a module called
2. The function ``isfrozen(obj)`` -- returns ``True`` if ``obj`` is immutable
-2.5. The function ``isfreezable(obj)`` -- returns ``True`` if all objects reachable from ``obj`` can be frozen
-
3. The type ``NotFreezable`` which is an empty type which cannot be made immutable and can be used as a super class to classes whose instances should not be possible to freeze
4. The type ``NotFreezableError`` which is raised on an attempt to mutate an immutable object
@@ -242,15 +240,6 @@ wishes to support immutability needs updating. The downside of the
opt-in model is that a large part of all Python libraries cannot
be (even nominally) made immutable (out-of-the-box).
-This PEP proposes to make support for immutability in C extensions
-and Python wrappers of classes which would otherwise not be freezable
-opt-in through a whitelisting mechanism implemented through the
-function ``register_freezable(type)`` in the ``immutable`` module.
-
-Note that it is possible to mix modules and types that support
-immutability with those that do not, as long as this does not
-breaks strictness.
-
Strictness
----------
@@ -382,6 +371,49 @@ starting places are ``object.c`` `[1]`_ and ``dictobject.c`` `[2]`_.
.. _[2]: https://github.com/mjp41/cpython/pull/51/files#diff-b08a47ddc5bc20b2e99ac2e5aa199ca24a56b994e7bc64e918513356088c20ae
+Expected Usage of Immutability
+------------------------------
+
+The main motivation for adding immutability in this PEP is to
+facilitate concurrent programming in Python. This is not something
+that Python's type system currently supports -- developers have to
+rely on other (i.e. not type-driven) methods to communicate around
+thread-safety and locking protocols. We expect that the same
+methodology works for immutable objects with the added benefit
+that mistakes lead to exceptions rather than incorrectness bugs or
+crashes. As the Python community adopts immutability, we expect to
+learn about the patterns that arise and this can inform e.g. how
+to develop tools, documentation, and types for facilitating
+programming with immutable objects in Python.
+
+We expect that libraries that for example want to provide intended
+constants may adopt immutability as a way to guard against someone
+say re-defining pi. Freezing a module's state can be made optional
+(opt-in or opt-out) so that the option of re-defining pi can be
+retained.
+
+If immutability is adopted widely, we would expect libraries to
+contain a section that detail what types etc. that it provides
+that can be made immutable or not. If Python's type system adds
+support for (say) distinguishing between must-be-mutable,
+must-be-immutability, and may-be-immutable, such annotations can
+be added to the documentation of a library's public API.
+
+If a library relies on user-provided data to be immutable, we
+expect the appropriate pattern is to check that the data is
+immutable and if not raising an exception rather than to make the
+data immutable inside the library code. This pushes the obligation
+to the user in a way that will not lead to surprises due to data
+becoming immutable under foot.
+
+We expect programmers to use immutability to facilitate safe
+communication between threads, and for safe sharing of data
+between threads. In both cases, we believe it is convenient to be
+able to freeze a data structure in-place and share it, and we
+expect programmers to have constructed these data structures with
+this use case in mind.
+
+
Deep Freezing Semantics
=======================
@@ -657,9 +689,6 @@ Implementation Details
cycle detection, and marks objects appropriately, and backs
out on failure, possibly partially freezing the object graph.
-2.5. Add the ``isfreezable(obj)`` function which checks that all
- objects reachable from ``obj`` can be frozen.
-
3. Add the ``register_freezable(type)`` function that is used to
whitelist types implemented as C extensions, permitting their
instances to be made immutable.
@@ -751,6 +780,14 @@ More Rejected Alternatives
logic and/or temporal interactions which can be hard to
test and reproduce.
+Another rejected idea was to provide a function ``isfreezable(obj)`` which
+returns ``True`` if all objects reachable from ``obj`` can be made
+immutable. This was rejected because free-threaded Python permits
+data-races during freezing. This means that the result of the check
+can be non-deterministic. A better way is to simply try to make
+an object immutable and catch the exception if the object could not
+be frozen.
+
A Note on Modularisation
========================
@@ -801,6 +838,187 @@ mutable and access to shared immutable objects can race on accesses
to weak references.
+Hashing
+=======
+
+Deep immutability opens up the possibility of any freezable object being
+hashable, due to the fixed state of the object graph making it possible to compute
+stable hash values over the graph as is the case with ``tuple`` and ``frozenset`` . However,
+there are several complications (listed below) which should be kept in mind for any future
+PEPs which build on this work at add hashability for frozen objects:
+
+
+Instance versus Type Hashability
+--------------------------------
+
+At the moment, the test for
+`hashability <https://docs.python.org/3/glossary.html#term-hashable>`__
+is based upon the presence (or absence) of a ``__hash__`` method and an
+``__eq__`` method. Places where ``PyObject_HashNotImplemented`` is currently
+used would need to be modified as appropriate to have a contextual logic
+which provides a default implementation that uses ``id()`` if the object
+instance has been frozen, and throws a type error if not.
+
+This causes issues with type checks, however. The check of
+``isinstance(x, Hashable)`` would need to become contextual, and
+``issubclass(type(x), Hashable)`` would become underdetermined for
+many types. Handling this in a way that is not surprising will require
+careful design considerations.
+
+
+Equality of Immutable Objects
+-----------------------------
+
+One consideration with the naive approach (*i.e.*, hash via ``id()``) is
+that it can result in confusing outcomes. For example, if there were
+to be two lists:
+
+.. code-block:: python
+
+ a = [1, 2, 3, 4]
+ b = [1, 2, 3, 4]
+ assert(hash(a) == hash(b))
+
+There would be a reasonable expectation that this assertion would be true,
+as it is for two identically defined tuples. However, without a careful
+implementation of ``__hash__`` and ``__eq__`` this would not be the case.
+Our opinion is that an approach like that used in ``tuplehash`` is
+recommended in order to avoid this behavior.
+
+
+Decorators of Immutable Functions
+=================================
+
+One natural issue that arises from deeply immutable functions is the
+state of various objects which are attached to them, such as decorators.
+In particular, the case of ``lru_cache`` is worth investigating. If the cache
+is made immutable, then freezing the function has essentially disabled the
+functionality of the decorator. This might be the correct and desirable
+functionality, from a thread safety perspective! In practice, we see three
+potential approaches:
+
+1. The cache is frozen in its state at the point when freeze is called.
+ Cache misses will result in an immutability exception.
+
+2. Access to the cache is protected by a lock to ensure thread safety
+
+3. There is one version of the cache per interpreter (*i.e.*, the cache is thread local)
+
+There are arguments in favor of each. Of them, (3) would
+require additional class to be added (*e.g.*, via the ``immutable`` module)
+which provides "interpreter local" dictionary variable that can be safely
+accessed by whichever interpreter is currently calling the immutable function.
+We have chosen (1) in order to provide clear feedback to the programmer that
+they likely do not want to freeze a function which has a (necessarily) mutable
+decorator or other object attached to it. It is likely not possible to make
+all decorators work via a general mechanism, but providing some tools to
+provide library authors with the means to provide a better experience for
+immutable decorators is in scope for a future PEP building on this work.
+
+
+Deferred Ideas
+==============
+
+Copy-on-Write
+-------------
+
+It *may* be possible to enforce immutability through copy-on-write.
+Such a system would not raise an exception on ``x.f = y`` when
+``x`` points to an immutable object, but rather copy the contents
+of ``x`` under the hood. Essentially, ``x.f = y`` turns into ``x =
+deep_copy(x); x.f = y``. While it is nice to avoid the error, this
+can also have surprising results (e.g. loss of identity of ``x``),
+is less predictable (suddenly the time needed to execute ``x.f = y``
+becomes proportional to the object graph rooted in ``x``) and may
+make code harder to reason about.
+
+
+Typing
+------
+
+Support for immutability in the type system is worth exploring in
+the future. Especially if Python adopts an ownership model that
+enables reasoning about aliasing, see `Data-Race Free Python`_
+below.
+
+Currently in Python, ``x: Foo`` does not give very strong
+guarantees about whether ``x.bar(42)`` will work or not, because
+of Python's strong reflection support that permits changing a
+class at run-time, or even changing the type of an object.
+Making objects immutable in-place exacerbates this situation as
+``x.bar(42)`` may now fail because ``x`` has been made immutable.
+However, in contrast to failures due to reflective changes of
+a class, a ``NotFreezableError`` will point to the place in the
+code where the object was frozen. This should facilitate debugging.
+
+In short: the possibility of making objects immutable in-place
+does not weaken type-based reasoning in Python on a fundamental
+level. However, if immutability becomes very frequently used, it
+may lead to the unsoundness which already exists in Python's current
+typing story surfacing more frequently. As alluded to in the
+future work on `Data-Race Free Python`_, this can be mitigated by
+using region-based ownership.
+
+There are several challenges when adding immutability to a type
+system for an object-oriented programming language. First, self
+typing becomes more important as some methods require that self is
+mutable, some require that self is immutable (e.g. to be
+thread-safe), and some methods can operate on either self type.
+The latter subtly needs to preserve the invariants of immutability
+but also cannot rely on immutability. We would need a way of
+expressing this in the type system. This could probably be done by
+annotating the self type in the three different ways above --
+mutable, immutable, and works either way.
+
+A possibility would be to express the immutable version of a type
+``T`` as the intersection type ``immutable & T`` and a type that
+must preserve immutability but may not rely on it as the union
+of the immutable intersection type with its mutable type
+``(immutable & T) | T``.
+
+Furthermore, deep immutability requires some form of "view-point
+adaption", which means that when ``x`` is immutable, ``x.f`` is
+also immutable, regardless of the declared type of ``f``.
+View-point adaptation is crucial for ensuring that immutable
+objects treat themselves correctly internally and is not part of
+standard type systems (but well-researched in academia).
+
+Making ``freeze`` a soft keyword as opposed to a function `has
+been proposed
+<https://discuss.python.org/t/adding-deep-immutability/92011/71>`_
+to facilitate flow typing. We believe this is an excellent
+proposal to consider for the future in conjunction with work on
+typing immutability.
+
+
+Naming
+======
+
+We propose to call deep immutability simply "immutability". This
+is simple, standard, and sufficiently distinguishable from other
+concepts like frozen modules.
+
+We also propose to call the act of making something immutable
+"freezing", and the function that does so ``freeze()``. This is
+the same as used in JavaScript and Ruby and is considerably
+snappier than ``make_immutable()`` which we suspect would be
+immediately shortened in the community lingo. The major concern
+with the freeze verb is that immutable objects risk being referred
+to as "frozen" which then comes close to frozen modules (bad link)
+and types like ``frozenset`` (good link).
+
+While naming is obviously important, the names we picked initially
+in this PEP are not important and can be replaced. A good short
+verb for the action seems reasonable. Because the term immutable
+is so standard, we should think twice about replacing it with
+something else.
+
+Qualifying immutability and freezing with an additional "deep" (as
+proposed `here
+<https://discuss.python.org/t/adding-deep-immutability/92011/6>`_)
+seems like adding extra hassle for unclear gains.
+
+
Future Extensions
=================
@@ -915,6 +1133,18 @@ is to make it impossible for Python programs to have data races.
Support for deeply immutable data is the first important step
towards this goal.
+The region-based ownership that we propose can be used to restrict
+freezing to only be permitted on regions which are isolated. If
+such a restriction is built into the system, then there will be a
+guarantee that freezing objects will not turn affect references
+elsewhere in the system (they cannot exist when the region is
+isolated). Such a design can also be used to track immutability
+better in a type system and would be able to deliver a guarantee
+that a reference of a mutable type never points to an immutable
+object, and conversely. These points will be unpacked and made
+more clear in the PEP for the ownership model.
+
+
Reference Implementation
========================
@@ -924,7 +1154,6 @@ Reference Implementation
There are some discrepancies between this PEP and the reference
implementation, including:
-- The ``isfreezable(obj)`` function is not yet implemented.
- The ``NotFreezable`` type is currently freezable (but inheriting
from it stops instances of the inheriting class from being made immutable).