PEP: 351
Title: The freeze protocol
Version: 2.5
Last-Modified: $Date$
Author: Barry Warsaw <[email protected]>
Status: Rejected
Type: Standards Track
Content-Type: text/x-rst
Created: 14-Apr-2005
Post-History:


Abstract
========

This PEP describes a simple protocol for requesting a frozen,
immutable copy of a mutable object.  It also defines a new built-in
function which uses this protocol to provide an immutable copy on any
cooperating object.


Rejection Notice
================

This PEP was rejected.  For a rationale, see `this thread on python-dev`_.

. _this thread on python-dev: https://mail.python.org/pipermail/python-dev/2006-February/060793.html


Rationale
=========

Built-in objects such dictionaries and sets accept only immutable
objects as keys.  This means that mutable objects like lists cannot be
used as keys to a dictionary.  However, a Python programmer can
convert a list to a tuple; the two objects are similar, but the latter
is immutable, and can be used as a dictionary key.

It is conceivable that third party objects also have similar mutable
and immutable counterparts, and it would be useful to have a standard
protocol for conversion of such objects.

sets.Set objects expose a "protocol for automatic conversion to
immutable" so that you can create sets.Sets of sets.Sets.  :pep:`218`
deliberately dropped this feature from built-in sets.  This PEP
advances that the feature is still useful and proposes a standard
mechanism for its support.


Proposal
========

It is proposed that a new built-in function called freeze() is added.

If freeze() is passed an immutable object, as determined by hash() on
that object not raising a TypeError, then the object is returned
directly.

If freeze() is passed a mutable object (i.e. hash() of that object
raises a TypeError), then freeze() will call that object's
__freeze__() method to get an immutable copy.  If the object does not
have a __freeze__() method, then a TypeError is raised.


Sample implementations
======================

Here is a Python implementation of the freeze() built-in::

   def freeze(obj):
       try:
           hash(obj)
           return obj
       except TypeError:
           freezer = getattr(obj, '__freeze__', None)
           if freezer:
               return freezer()
           raise TypeError('object is not freezable')``

Here are some code samples which show the intended semantics::

   class xset(set):
       def __freeze__(self):
           return frozenset(self)

   class xlist(list):
       def __freeze__(self):
           return tuple(self)

   class imdict(dict):
       def __hash__(self):
           return id(self)

       def _immutable(self, *args, **kws):
           raise TypeError('object is immutable')

       __setitem__ = _immutable
       __delitem__ = _immutable
       clear       = _immutable
       update      = _immutable
       setdefault  = _immutable
       pop         = _immutable
       popitem     = _immutable

   class xdict(dict):
       def __freeze__(self):
           return imdict(self)

   >>> s = set([1, 2, 3])
   >>> {s: 4}
   Traceback (most recent call last):
     File "<stdin>", line 1, in ?
   TypeError: set objects are unhashable
   >>> t = freeze(s)
   Traceback (most recent call last):
     File "<stdin>", line 1, in ?
     File "/usr/tmp/python-lWCjBK.py", line 9, in freeze
   TypeError: object is not freezable
   >>> t = xset(s)
   >>> u = freeze(t)
   >>> {u: 4}
   {frozenset([1, 2, 3]): 4}
   >>> x = 'hello'
   >>> freeze(x) is x
   True
   >>> d = xdict(a=7, b=8, c=9)
   >>> hash(d)
   Traceback (most recent call last):
     File "<stdin>", line 1, in ?
   TypeError: dict objects are unhashable
   >>> hash(freeze(d))
   -1210776116
   >>> {d: 4}
   Traceback (most recent call last):
     File "<stdin>", line 1, in ?
   TypeError: dict objects are unhashable
   >>> {freeze(d): 4}
   {{'a': 7, 'c': 9, 'b': 8}: 4}


Reference implementation
========================

Patch 1335812_ provides the C implementation of this feature.  It adds the
freeze() built-in, along with implementations of the __freeze__()
method for lists and sets.  Dictionaries are not easily freezable in
current Python, so an implementation of dict.__freeze__() is not
provided yet.

. _1335812: http://sourceforge.net/tracker/index.php?func=detail&aid=1335812&group_id=5470&atid=305470

Open issues
===========

- Should we define a similar protocol for thawing frozen objects?

- Should dicts and sets automatically freeze their mutable keys?

- Should we support "temporary freezing" (perhaps with a method called
 __congeal__()) a la __as_temporarily_immutable__() in sets.Set?

- For backward compatibility with sets.Set, should we support
 __as_immutable__()?  Or should __freeze__() just be renamed to
 __as_immutable__()?


Copyright
=========

This document has been placed in the public domain.



.
  Local Variables:
  mode: indented-text
  indent-tabs-mode: nil
  sentence-end-double-space: t
  fill-column: 70
  End: