Hi Travis,

On 2013-05-02, Travis Scrimshaw <tsc...@ucdavis.edu> wrote:
>    I don't think so... This would be useful for other mutable things such 
> as matrices and simplicial complex rather than explicitly check it in the 
> methods themselves.

OK. Thanks to sage.misc.decorators.sage_wraps, it will be easy to
create such decorator.

Or two decorators: The first complains if the instance is mutable (can be used
on __hash__), the second complains if the instance is immutable (can be used
on a mutating method). I suggest the name "@invariant" for the first and
"@mutabor" (this is Latin for "I will be changed") for the second decorator.

Here is a more extreme idea. Since the decorated methods can be overridden
in subclasses, I thought of using a metaclass.

The following syntax could be made work:

class A:                                          
    __metaclass__ = MutabilityType(['bla'],['__hash__'])
    def __init__(self, val):
        self._m = val
    def bla(self, new_val):
        self._m = new_val
    def __hash__(self):
        return hash(self._m)
sage: type(A)
__main__.MutabilityMetaclass(mutations=('bla',), invariants=('__hash__',))
sage: a = A(5)

The semantics behind this syntax is:

- The metaclass provides the new class with methods is_mutable, is_immutable
  and set_mutability:
    sage: a.is_mutable()
    True
    sage: a.set_mutability(False)
    sage: a.is_mutable()
    False

- The metaclass ensures that instances of the new class must be mutable
  for calling the method bla:
  sage: a.bla(6)
  Traceback (most recent call last):
  ...
  AssertionError: <class '__main__.A'> instance is immutable, <function bla at 
0x5274488> must not be called

- The metaclass ensures that instances of the new class must be
  immutable for calling the hash:
  age: hash(a)
  5
  sage: a.set_mutability(True)
  sage: hash(a)
  Traceback (most recent call last):
  ...
  AssertionError: <class '__main__.A'> instance is mutable, <function __hash__ 
at 0x52746e0> must not be called

- Subclasses inherit this behaviour:
  class B(A):                                       
      def bla(self, new_val):                             
          print "overridden"
          self._m = new_val
      def __hash__(self):
          print "overridden"
          return hash(self._m)
  sage: b = B(5)
  sage: hash(b)
  Traceback (most recent call last):
  ...
  AssertionError: <class '__main__.B'> instance is mutable, <function __hash__ 
at 0x548e938> must not be called
  sage: b.set_mutability(False)
  sage: hash(b)
  overridden
  5
  sage: type(B)
  __main__.MutabilityMetaclass(mutations=('bla',), invariants=('__hash__',))

So, the advantage over a decorator is that the behaviour wrt. mutability
is inherited by subclasses, and that the class A (or B) can actually tell
exactly which methods are required to be mutable or immutable.

The disadvantage is that metaclasses tend to confuse people, and it
would be difficult to make it cooperate with existing metaclasses
(such as the one used by UniqueRepresentation). This could be implemented
by using meta-metaclasses, that create cooperating metaclasses
dynamically...

What do people think?
[ ] @invariant and @mutabor is fine
[ ] these (meta-)metaclasses are totally crazy
[ ] others [please elaborate]

Best regards,
Simon


-- 
You received this message because you are subscribed to the Google Groups 
"sage-devel" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sage-devel+unsubscr...@googlegroups.com.
To post to this group, send email to sage-devel@googlegroups.com.
Visit this group at http://groups.google.com/group/sage-devel?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to