On Mon, 09 Jun 2014 16:48:09 +0200
leif <not.rea...@online.de> wrote:

> Volker Braun wrote:
> > On Monday, June 9, 2014 12:54:56 AM UTC+1, vdelecroix wrote:
> >
> >     sage: {3, AA(3)}
> >     {3, 3}
> >
> >
> > IMHO you should never put sage objects with different parents into an
> > associative container.
> 
> Although that's presumably an anti-pythonic attitude, +1.  (Provided 
> they *won't* have sex with each other that is.)

IMHO this should be disallowed. All objects that implement __hash__
should live up to contract of __hash__, which is that a==b implies
hash(a)==hash(b). If this contract is violated, then the object is
not worthy of being called hashable and sage should not pretend that it
is hashable. It is too easy to forget to coerce and get a dict that
violates a==b => d[a]==d[b], especially since this coercion is not
necessary in python and not for ZZ->QQ, ZZ->RR, ZZ->CC, and RR->CC.

I would propose that instead of __hash__ something like _sage_hash
should be used that implements a hash satisfying 
 self == other and self.parent() == other.parent()
    implies
 self._sage_hash() == other._sage_hash().
Then there could be sage specific associative containers using this
hash. For instance something like this:

d = MyDict(CoerceTo(complex))
d[1] = 0
d[1+I] = 3

e = MyDict(CoerceTo(ZZ))
e[1] = 0
e[1+I] = 3 # ERROR!

def sage_hash(x):
        #TODO: handle tuples, ...
        try:
                return x._sage_hash()
        except AttributeError:
                return hash(x)

class Shim:
        def __init__(self, hash_manager, data):
                self.hm = hash_manager
                self.data = data
        def __hash__(self):
                return self.hm.hash(self)
        def __eq__(self, other):
                return self.hm.equal(self, other)

class MyDict:
        def __init__(self, hash_manager, data = []):
                self.hm = hash_manager
                self.content = dict()
                #TODO initialize with stuff in data

        def __setitem__(self, key, val):
                if not self.hm.acceptable(key):
                        throw "cannot accomodate this kind of key"
                self.content[Shim(hm, key)] = val

class CoerceTo:
        def __init__(self, target):
                self.target = target
        def acceptable(self, data):
                try:
                        data = target(data)
                except:
                        return False
                else:
                        return True
        def hash(self, data):
                data = target(data)
                return sage_hash(data)
        def equal(self, other):
                return target(self) == target(data)


The hash_manager could be lazy about what kinds of objects
it accepts. For instance a MyDict with the manager below would accept
anything with the same parent as the first element:

class CoerceToFirst:
        def __init__(self):
                self.inner = None
        def acceptable(self, data):
                if self.inner is None:
                        # FIXE: handle python types
                        self.inner = CoerceTo(data.parent())
                return self.inner.expand(data)
        def __getattr__(self, name):
                return getattr(self.inner, name)

It is also possible to have a manager that expands the kinds of keys
that it accept, as long as they are compatible with what was received
before, or one that doesn't check anything and just calls sage_hash,
assuming that the contract is satisfied for all relevant keys. (This
would avoid having to change all cached functions.)


Regards,

Erik Massop

-- 
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.
For more options, visit https://groups.google.com/d/optout.

Reply via email to