> > > > change "To coerce from the category down to the parent" --> "To coerce > from > > the abstraction layer to the parent". In this abstraction layer (which > is > > close to the categories now available, but not quite the same), we would > > have a list of methods for the parent class and the element class of a > > Category C. Now say you have parentA and parentB both implementing the > same > > category C. As part of its definition, parentA c/should tell how to > > initialize it using exclusively methods from this abstraction layer. But > > parentB implements some of these methods. With any luck there might be > > enough overlap that we automatically get a coercion from parentB -> > parentA. > > This would work by going from parentB "up" to the abstraction layer's > method > > and then "down" onto parentA. I mean up and down not in the sense of > class > > hierarchy but in terms of abstraction level. > > Sounds interesting, lets make this idea concrete. So, lets assume we > have the category of Rings, and we want to define the category of > (univariate?) polynomials over a Ring that extends (literally or not) > the category of Rings. What (additional) contract on Parents would you > require/enforce? For example, one could require a method converting > from a list/dict of ring elements to the polynomial, and the inverse. > A constant map (which of course could be implemented in terms of the > above)? GCD if the basering is a field? >
You are asking about a different kind of coercion from the one I was talking about (that's part of my point: some are are purely at the concrete/Parents level -when the category does not change, I think these are called conversions as well-, some are purely at the abstract/category level, while some others are compositions of these two "pure" kinds of coercions). Let's use decorators to describe the two "pure" kinds of coercions, with prototype of the code that would run: 1) Purely concrete level - the kind I was talking about - the category stays the same, but the implementation (i.e. parents) changes. Even in this case this is dealt with by specifying in terms of the category what arguments the constructor takes in each of the parents. class CategoryC(Category): class ElementMethods: def invariantM(self): pass .... def invariantX(self): pass def invariantZ(self): pass class Parent1(Parent, category = CategoryC): # unrelated point: for me it makes more sense to specify the category here. # syntax like this is made possible because this is specified in a module where the metaclass # used knows how to deal with the category keyword in the "bases". @from_category(lambda self: type_expected_for_arg1(self.invariantM()), lambda self: type_expected_for_arg2(self.invariantN())) # syntax to improve # means that arg1 below results from applying the first function to any object of the same category CategoryC (if possible) # similar for arg2 def _element_constructor_(self, arg1, arg2): pass class Element1(Parent1.Element) def invariantM(self): pass def invariantN(self): pass def invariantO(self): pass def invariantP(self): pass So CategoryC has many invariants, M to Z. Parent1 has a constructor for the elements that takes as input the invariants M and N in some specified types, and its element is then able to compute invariants O and P (let's say in some standardized types, but maybe these methods could be decorated as well with type specifications). Parent2 (unshown) takes as input the invariants O, P and computes Q, R, and so on for a bunch of Parents One way to look ar this is that Parent1 provides the following conversions: - the composite (CategoryC.invariantM, CategoryC.invariantN) to (CategoryC.invariantO) - the composite (CategoryC.invariantM, CategoryC.invariantN) to (CategoryC.invariantP) - the composite (CategoryC.invariantM, CategoryC.invariantN) to the composite (CategoryC.invariantO, CategoryC.invariantP) Parent2 provides different ones, and so on for the other Parents. All together this could make for a full strategy to recover some set of invariants from some other set. Coercions would allow here to construct a composite parent from many different parents sharing the same category. It might feel artificial, but for the sage+databases workshop in Edinburgh in January, I am thinking along those lines. A Parent would still correspond to a specific implementation. Not of code, but rather an implementation of a specific schema for a subset of the invariants of the category being modeled. People come up with many schemas for math data, and actually it would even make sense to consider them to be dynamic. I want to avoid at all costs forcing people to look at each other's data formats (unreasonable burden) to create conversions. That's why I am looking at the categories framework as providing a common abstract layer while staying sufficiently generic and expressive. (Note that doing things this way would make it possible to generate systematic tests that the results of computing invariants in different implementations are the same). 2) Now, this is the kind you were asking about: coercions between Parents with different underlying categories. You said: So, lets assume we have the category of Rings, and we want to define the category of (univariate?) polynomials over a Ring that extends (literally or not) the category of Rings. What (additional) contract on Parents would you require/enforce? For this coercion, nothing would be required of the Parents: it has nothing to do with them! Someone would have to say in the category framework that there is always a morphism from a ring R to a graded algebra over that ring R, and register this as a coercion (This would have to happen in a GradedAlgebra functor analogous to the Algebra functor). Is this unrealistic? Does some of this already happen? Paul > > >> A while ago, I have created a public worksheet on "how to implement > >> basic algebraic structures in Sage", using category framework and > >> coercion. > >> I think at some point it was supposed to become part of the > >> documentation, but I think we lost momentum. > > > > I just found what you posted a year ago. That's great! As I said, > exactly > > this should be available somewhere official and marked as current. > > > > > >> > >> Both decorators and metaclasses are used in Sage. Note one problem, > >> though: > >> If you want to create a new class inheriting from classes with distinct > >> metaclasses, then Python complains. > > > > Yes. Yes. > > > >> > >> I had experimental code to overcome that problem, namely by using > >> meta-metaclasses that dynamically create a new common metaclass out of > any > >> list of metaclasses. Hence, if A and B are classes with metaclasses MA > and > >> MB, then you can define > >> class C(A,B) > >> so that C gets a dynamically created metaclass MC that is a sub-class > of > >> both MA and MB. The idea is that both MA and MB (which *are* > >> metaclasses) have a common meta-metaclass MM, and MM is able to create > a > >> new metaclass MC out of MA and MB. > > > > Yes. Since these metaclasses are there to specifically abstract some > > concerns's implementation details, these metaclasses are aspects. Then > this > > step is aspect weaving. > > > >> > >> In that way, you could have metaclasses for different purposes, and can > >> freely combine them: You want a class that is pickled by construction > >> and whose instances are cached? Then simply combine > >> DynamicMetaclass and ClasscallMetaclass. No need to separately/manually > >> define DynamicClasscallMetaclass anymore - the meta-metaclass would do > >> that for you. > > > > Yes. I was thinking along the same lines. Do you think the ordering of > the > > metaclasses should matter? > > > >> > >> But it seems people found the idea of meta-metaclasses a bit > confusing... > > > > Well, it is, but I think it's a valid solution. And 95% of people won't > have > > to worry about these meta metaclasses: the interface could be a > customizable > > MagicObject for instance. The cost is on the side of the metaclass, > whose > > complexity is fairly high. The reward is that the decorators are trivial > to > > use. > > > >> > >> > >> > - "This will slow down the one class I am using a lot": Not if it's > >> > done > >> > right. In fact, if it's done very right it would speed up > considerably > >> > everything, > >> > >> +1. > >> > >> One example: One can write a FastHashMetaclass. If you then take a > >> python class A that has a `__hash__` method, and apply to A the > >> FastHashMetaclass, then A.__hash__ would automatically be turned into a > >> fast Cython hash that moreover is cached (so that the hash value does > >> not need to be computed repeatedly). > >> > >> Best regards, > >> Simon > >> > >> > > -- > > You received this message because you are subscribed to the Google > Groups > > "sage-devel" group. > > To post to this group, send email to > > sage-...@googlegroups.com<javascript:>. > > > To unsubscribe from this group, send email to > > sage-devel+...@googlegroups.com <javascript:>. > > Visit this group at http://groups.google.com/group/sage-devel?hl=en. > > > > > -- You received this message because you are subscribed to the Google Groups "sage-devel" group. To post to this group, send email to sage-devel@googlegroups.com. To unsubscribe from this group, send email to sage-devel+unsubscr...@googlegroups.com. Visit this group at http://groups.google.com/group/sage-devel?hl=en.