* orthogonal inheritance and extension aggregates @ 2013-07-19 17:46 Felix Krause 2013-07-19 18:00 ` Adam Beneschan 2013-07-19 19:05 ` Dmitry A. Kazakov 0 siblings, 2 replies; 11+ messages in thread From: Felix Krause @ 2013-07-19 17:46 UTC (permalink / raw) I try to implement some feature via orthogonal inhertance like this: generic type Parent (<>) is abstract tagged private; package Extension is type Extended is abstract new Parent with private; -- operations of the extended type here private type Extended is abstract new Parent with record -- … end record; end Extension; Now for testing, I tried to apply this on a Vector: package Int_Vectors is new Ada.Container.Vectors (Natural, Integer); package Base is new Extension (Int_Vectors.Vector); type Child is new Base.Extended with null record; At the declaration of Child, GNAT tells me "type must be declared abstract or "To_Vector" overridden". Can someone explain why this happens? As I understand it, To_Vector should be automatically defined for the derived type. Is this because the intermediate type Extended is declared abstract? Or because To_Vector dispatches in the return type? Anyway, I tried to declare the function for the type Child like this: function To_Vector (Length : Ada.Containers.Count_Type) return Child is begin return Child'(Base.Extended'(Int_Vectors.To_Vector (Length) with others => <>) with null record); end To_Vector; Firstly, this looks rather awkward. Secondly, it doesn't compile: "expected private type Extended […] found a composite type". I guess this does not work because the type Extended is hidden in the private part of Extension. Is it possible to define To_Vector in this context to do exactly what the base function does? -- Felix Krause http://flyx.org/ ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-19 17:46 orthogonal inheritance and extension aggregates Felix Krause @ 2013-07-19 18:00 ` Adam Beneschan 2013-07-22 16:02 ` Adam Beneschan 2013-07-19 19:05 ` Dmitry A. Kazakov 1 sibling, 1 reply; 11+ messages in thread From: Adam Beneschan @ 2013-07-19 18:00 UTC (permalink / raw) On Friday, July 19, 2013 10:46:38 AM UTC-7, Felix Krause wrote: > I try to implement some feature via orthogonal inhertance like this: > > generic > type Parent (<>) is abstract tagged private; > package Extension is > type Extended is abstract new Parent with private; > > -- operations of the extended type here > private > type Extended is abstract new Parent with record > -- … > end record; > end Extension; > > Now for testing, I tried to apply this on a Vector: > > package Int_Vectors is new Ada.Container.Vectors (Natural, Integer); > package Base is new Extension (Int_Vectors.Vector); > > type Child is new Base.Extended with null record; > > > At the declaration of Child, GNAT tells me "type must be declared > abstract or "To_Vector" overridden". Can someone explain why this > happens? As I understand it, To_Vector should be automatically defined > for the derived type. Is this because the intermediate type Extended is > declared abstract? Or because To_Vector dispatches in the return type? This looks like a GNAT bug to me. -- Adam ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-19 18:00 ` Adam Beneschan @ 2013-07-22 16:02 ` Adam Beneschan 2013-07-22 18:44 ` Dmitry A. Kazakov 2013-07-22 18:59 ` Adam Beneschan 0 siblings, 2 replies; 11+ messages in thread From: Adam Beneschan @ 2013-07-22 16:02 UTC (permalink / raw) On Friday, July 19, 2013 11:00:34 AM UTC-7, Adam Beneschan wrote: > On Friday, July 19, 2013 10:46:38 AM UTC-7, Felix Krause wrote: > > > I try to implement some feature via orthogonal inhertance like this: > > > > generic > > type Parent (<>) is abstract tagged private; > > package Extension is > > type Extended is abstract new Parent with private; > > > > -- operations of the extended type here > > private > > type Extended is abstract new Parent with record > > -- … > > end record; > > end Extension; > > > > > > Now for testing, I tried to apply this on a Vector: > > > package Int_Vectors is new Ada.Container.Vectors (Natural, Integer); > > package Base is new Extension (Int_Vectors.Vector); > > > type Child is new Base.Extended with null record; > > > At the declaration of Child, GNAT tells me "type must be declared > > abstract or "To_Vector" overridden". Can someone explain why this > > happens? As I understand it, To_Vector should be automatically defined > > for the derived type. Is this because the intermediate type Extended is > > declared abstract? Or because To_Vector dispatches in the return type? > > This looks like a GNAT bug to me. OK, I goofed. I forgot that the rules were different when it comes to inherited functions that return tagged types. GNAT's error message wasn't really helpful. The more I look at it, the more I think this is a missing language feature that is not going to be easy to fix. Normally, if you have a tagged type T and an operation that is a function F that returns T, and in some other package P2 you defined a tagged type T2 that is derived from T and adds components to it, you *must* provide an overriding function F that operates on T2 and (presumably) sets up those additional fields. If the language allowed you to simply inherit F, that version of F wouldn't set up the new fields. If you declare T2 to be abstract, the language will let you get away with not overriding F; the inherited F will then be abstract. I think this might have been a mistake (that is, I think 3.9.3(5) should have applied only to abstract subprograms, not to functions that return controlling types, at least when the parent type is non-abstract). The package that declares the new fields in the type (i.e. P2) should be responsible for defining the operations that set up the new fields. This is especially the case when the extension fields are declared in a private part, since other packages that define concrete derived types from T2 have no way to set up the extension fields defined for T2 (unless they happen to be in child packages of P2). Generics with formal derived types just make everything worse, as you've noticed, because now, at the time the generic is declared, the compiler can't tell what functions might be present that return the tagged type. When the generic is instantiated, the question needs to be asked, "All right, we've just inherited some To_Vector functions that return this new Extended type; who's going to be responsible for making sure the new fields are set up"? And you have two packages pointing fingers at each other. Extension says it can't do it, because it doesn't know what functions will get inherited. And the package that contains the instantiation can't do it, because it can't see the fields that need to be set up. Even if Extension were a public extension, it still wouldn't work, because the language doesn't provide a way for code to instantiate a generic and then define new overriding primitives *inside* the generic. But in the present case, where it's a private extension, any solution would have to involve some kind of new and interesting kind of cooperation between the two packages, since neither one has access to all the needed information. The only solution I can think of is to add some sort of "default extension initializer" feature that Extension could provide, and then the inherited To_Vector functions that call the parent To_Vector and then the "default extension initializer" could be implicitly defined. But I don't like this, since a single routine that initializes the extension components in the same way for all functions may not be appropriate. So basically I think what you're trying to do just shouldn't be done in Ada, and frankly I think the generic instantiation should have been illegal, and it's a language hole that it lets you get through. I'll try to think about it and see if there's some other solution that better reflects the class of problem you're trying to solve, rather than being a hack to work around the rules. (Plus I'll try to think about how this might be handled in other languages, although no other language has the same kind of generic features as Ada so it might be difficult to find an exact parallel.) -- Adam ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-22 16:02 ` Adam Beneschan @ 2013-07-22 18:44 ` Dmitry A. Kazakov 2013-07-22 18:59 ` Adam Beneschan 1 sibling, 0 replies; 11+ messages in thread From: Dmitry A. Kazakov @ 2013-07-22 18:44 UTC (permalink / raw) On Mon, 22 Jul 2013 09:02:27 -0700 (PDT), Adam Beneschan wrote: > The more I look at it, the more I think this is a missing language feature > that is not going to be easy to fix. Yes. That feature has a name, it is "constructor". > The only solution I can think of is to add some sort of "default extension > initializer" feature that Extension could provide, The "initializer" needs parameters from which it initializes the extension. It is merely a constructor of the extension. > So basically I think what you're trying to do just shouldn't be done in Ada, That Ada offers no means to design opaque types which instances require initialization does not mean that this should not be done in Ada. It is a good and useful pattern, which the language should support and encourage. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-22 16:02 ` Adam Beneschan 2013-07-22 18:44 ` Dmitry A. Kazakov @ 2013-07-22 18:59 ` Adam Beneschan 2013-07-22 19:19 ` Adam Beneschan 2013-07-22 22:03 ` Felix Krause 1 sibling, 2 replies; 11+ messages in thread From: Adam Beneschan @ 2013-07-22 18:59 UTC (permalink / raw) OK, I've thought about this a little more. First of all, I think the problem might be unique to Ada. Ada allows you to write a generic that takes an arbitrary tagged type and defines a new extension of that type. The one other OOP language I'm most familiar with, Java, just doesn't allow this: public class ExtensionOf<T> extends T is ... is illegal. (T is a "type variable" which is pretty much the equivalent of a generic formal tagged type, and "extends" is how you write derived types; but "extends" doesn't allow "type variables" in its syntax.) If it did allow it, the new type wouldn't inherit T's constructors, since constructors just aren't inherited. Ada does try to inherit the "constructors", i.e. functions that return values of the type; but of course the inherited constructors make no sense because they cannot initialize extension components. Thus, an inherited constructor (for a concrete type) is illegal and must be overridden. But, as this example shows, this can't be done when the parent type is a generic formal type, since we don't know what functions might be defined for the actual. It doesn't make sense for the Extension generic package to try to provide versions of all the constructors that might be defined for the actual type. I think that the "right" way to handle this would be for Extension to define a function that takes a Parent object and returns an Extended object: function Create_Extended (Obj : Parent) return Extended; where Create_Extended would initialize fields and whatever other action is necessary, and then the code that uses it would do something like E1 : Extended := Create_Extended (To_Vector (...)); assuming that Extended were not declared "abstract". This doesn't solve the legality problem, though. So I'm thinking that the rules in 3.9.3 need to be relaxed. 3.9.3(4-6) says that for a derived type, if the parent type has any abstract subprograms or functions with a controlling result, then (1) if the derived type is abstract, the inherited subprogram is abstract; or (2) the subprogram must be overridden, except in null extension cases. What I'm thinking is that we need a (2a) that says something like: if the parent type is a generic formal tagged type or type extension, and the derived type is not a generic formal, and if the subprogram is not abstract [which means it must be a function with a controlling result], and if no overriding subprogram is explicitly provided, the subprogram is overridden with an implicit subprogram that does nothing but raise Program_Error. This should solve the problem in a case like this--where the language demands that some subprograms be overridden but does not provide any mechanism for doing so. I'm assuming that if this were the case, Extended would no longer need to be "abstract". (If it were, this change wouldn't be sufficient.) Any thoughts? Does this seem like a possibly worthwhile proposal? -- Adam ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-22 18:59 ` Adam Beneschan @ 2013-07-22 19:19 ` Adam Beneschan 2013-07-22 22:03 ` Felix Krause 1 sibling, 0 replies; 11+ messages in thread From: Adam Beneschan @ 2013-07-22 19:19 UTC (permalink / raw) On Monday, July 22, 2013 11:59:29 AM UTC-7, Adam Beneschan wrote: > What I'm thinking is that we need a (2a) that says something like: if the parent type is a generic formal tagged type or type extension, and the derived type is not a generic formal, and if the subprogram is not abstract [which means it must be a function with a controlling result], and if no overriding subprogram is explicitly provided, the subprogram is overridden with an implicit subprogram that does nothing but raise Program_Error. This should solve the problem in a case like this--where the language demands that some subprograms be overridden but does not provide any mechanism for doing so. I'm assuming that if this were the case, Extended would no longer need to be "abstract". (If it were, this change wouldn't be sufficient.) > I meant for this to apply only in an instance. I intended to say that, but it got left out somehow. (Any actual language wording would probably have to refer to "the actual type for a formal", or something like that.) -- Adam ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-22 18:59 ` Adam Beneschan 2013-07-22 19:19 ` Adam Beneschan @ 2013-07-22 22:03 ` Felix Krause 1 sibling, 0 replies; 11+ messages in thread From: Felix Krause @ 2013-07-22 22:03 UTC (permalink / raw) On 2013-07-22 18:59:29 +0000, Adam Beneschan said: > OK, I've thought about this a little more. First of all, I think the > problem might be unique to Ada. Ada allows you to write a generic that > takes an arbitrary tagged type and defines a new extension of that > type. The one other OOP language I'm most familiar with, Java, just > doesn't allow this: > > public class ExtensionOf<T> extends T is ... > > is illegal. (T is a "type variable" which is pretty much the > equivalent of a generic formal tagged type, and "extends" is how you > write derived types; but "extends" doesn't allow "type variables" in > its syntax.) Just as an example, this would be how one could do it in C++: template<class T> class extended : public T { int i; // some meaningless extension to the base class public: extended(const T& other) : T(other), i(42) {} }; class my_class : public extended<std::vector<int> > { my_class(size_type n) : extended(std::vector<int>(n)) {} }; I think there are some interesting things to note here: * C++ has no means of explicitly making the intermediate class abstract. However, if one would define at least one pure virtual (= abstract) method in there, it would be abstract, and the example would still work. The important thing here is that unlike Ada, C++ allows constructors for abstract types. In Ada, it is impossible to provide a generic Create_Extended function like you suggest if the type is abstract. * C++ makes heavy use of so-called copy constructors, ie. constructors that take another value of the same type as parameter and create a copy of that value. The compiler automatically generates a copy constructor if the class meets certain criterias. In this case, I use the copy constructor of std::vector to create a constructor of my_class that is similar to the corresponding constructor of std::vector. So, Ada's primary problem seems to be that one cannot define a constructor-like method for an abstract type. Now the Ada Style Guide at wikibooks tells us Consider splitting the initialization and construction of an object. [1] Let's try to port the idea of an extended creation method to this pattern: procedure Init (Obj : in out Extended; From : Parent'Class); It's important that Init is not a primitive operation of Extended, because child initialization methods may need additional arguments. So it should be defined in a nested package or something: package Initialization is procedure From_Parent (Obj : in out Extended; From : Parent'Class); end Initialization; By the way, the Style Guide advices the same for constructors (putting them in a nested or child package). While this seems to be a bit bloat, it does solve the inheritance problem. An important issue with this solution is that one should somehow keep track of the initialization status of the object (because if initialization does not happen at allocation automatically, the object may be used uninitialized). The tagged type should have a function "Initialized" that returns a Boolean. As soon as a tagged type does provide this mechanism, all child types need to implement it too (provide an own initialization function that calls one of its parent's initializers), so that this state is consistent throughout the type hierarchy. The problem is that the Ada language does not endorse this or any other pattern. In C++, copy constructors are the way to go because the language supports them aggressively. In Ada, it may be inconvenient to check Initialized every time before we use an object in any way (well, Ada 2012 has the Pre aspect for such things). And now the most important thing: For this to work in the case I described in my first post, the base type given as generic parameter needs to implement this pattern! Because From_Parent is not implementable with extension aggregates (one cannot assign a value to an abstract type), so we would need to have an initialization method for the Vector type. This is why Ada should endorse some pattern of object construction: If I define one myself, it may work with my own code, but it's just not interoperable with the standard library types or potentially third-party code. [1]: http://en.wikibooks.org/wiki/Ada_Style_Guide/Object-Oriented_Features#Constructors -- Felix Krause http://flyx.org/ ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-19 17:46 orthogonal inheritance and extension aggregates Felix Krause 2013-07-19 18:00 ` Adam Beneschan @ 2013-07-19 19:05 ` Dmitry A. Kazakov 2013-07-20 5:49 ` Randy Brukardt 1 sibling, 1 reply; 11+ messages in thread From: Dmitry A. Kazakov @ 2013-07-19 19:05 UTC (permalink / raw) On Fri, 19 Jul 2013 19:46:38 +0200, Felix Krause wrote: > I try to implement some feature via orthogonal inhertance like this: > > generic > type Parent (<>) is abstract tagged private; > package Extension is > type Extended is abstract new Parent with private; > > -- operations of the extended type here > private > type Extended is abstract new Parent with record > -- … > end record; > end Extension; > > Now for testing, I tried to apply this on a Vector: > > package Int_Vectors is new Ada.Container.Vectors (Natural, Integer); > package Base is new Extension (Int_Vectors.Vector); > > type Child is new Base.Extended with null record; > > At the declaration of Child, GNAT tells me "type must be declared > abstract or "To_Vector" overridden". Can someone explain why this > happens? As I understand it, To_Vector should be automatically defined > for the derived type. Is this because the intermediate type Extended is > declared abstract? Or because To_Vector dispatches in the return type? > > Anyway, I tried to declare the function for the type Child like this: > > > function To_Vector (Length : Ada.Containers.Count_Type) return Child is > begin > return Child'(Base.Extended'(Int_Vectors.To_Vector (Length) with > others => <>) with null record); > end To_Vector; > > Firstly, this looks rather awkward. Secondly, it doesn't compile: > "expected private type Extended […] found a composite type". I guess > this does not work because the type Extended is hidden in the private > part of Extension. Is it possible to define To_Vector in this context > to do exactly what the base function does? OK here are two different issues. 1. There are more than one To_Vector declared in Ada.Containers.Vectors. And there are other primitive operations there you must override: with Ada.Containers.Vectors; with Extension; package PE is package Int_Vectors is new Ada.Containers.Vectors (Natural, Integer); package Base is new Extension (Int_Vectors.Vector); type Child is new Base.Extended with null record; overriding function To_Vector (Length : Ada.Containers.Count_Type) return Child; overriding function To_Vector ( New_Item : Integer; Length : Ada.Containers.Count_Type ) return Child; overriding function "&" (Left : Child; Right : Child) return Child; overriding function "&" (Left : Integer; Right : Child) return Child; overriding function "&" (Left : Child; Right : Integer) return Child; overriding function "&" (Left, Right : Integer) return Child; overriding function Copy ( Source : Child; Capacity : Ada.Containers.Count_Type := 0 ) return Child; end PE; 2. This is a more serious problem. You use a private extension in Extension, which means that clients cannot use aggregates in order to create instances. Extension aggregates is a horrific thing in Ada. But nothing compared to the constructing function. At this point you must consider what are you going to fight. If aggregates, then make extension public. If functions then provide a constructing function in the Extension package. Beware that your case will be a *combination* of aggregates and constructing functions is totally broken in Ada. It might happen to work to you or not. Nobody, even a language lawyer could say in advance if and when it will. P.S. I don't know if Ada 2012 silently inherits from null extension. I heard that there was an intention to add the kludge. Though semantically broken it would save programs like yours. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-19 19:05 ` Dmitry A. Kazakov @ 2013-07-20 5:49 ` Randy Brukardt 2013-07-20 6:36 ` Dmitry A. Kazakov 0 siblings, 1 reply; 11+ messages in thread From: Randy Brukardt @ 2013-07-20 5:49 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:igo2b70i4ox.t5duix9weguc$.dlg@40tude.net... ... > P.S. I don't know if Ada 2012 silently inherits from null extension. I > heard that there was an intention to add the kludge. Though semantically > broken it would save programs like yours. That's actually an Ada 2005 feature - see 3.9.3(4/2). One that I was against because it introduces a maintenance problem (null extensions tend to turn into non-null extensions over time, and once that happens, the feature stops working). It appears that GNAT failed to properly implement this feature in the case described by the OP. OTOH, as soon as the OP starts adding components to the extension, they'll have the problem again. (I don't think it is practical to extend the predefined containers, particularly because it's impossible to derive a cursor type to go with the extended container -- so you either end up with broken typing or you have do everything twice. And there are a lot of functions that have to be overridden.) Randy. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-20 5:49 ` Randy Brukardt @ 2013-07-20 6:36 ` Dmitry A. Kazakov 2013-07-20 7:54 ` Niklas Holsti 0 siblings, 1 reply; 11+ messages in thread From: Dmitry A. Kazakov @ 2013-07-20 6:36 UTC (permalink / raw) On Sat, 20 Jul 2013 00:49:41 -0500, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:igo2b70i4ox.t5duix9weguc$.dlg@40tude.net... > ... >> P.S. I don't know if Ada 2012 silently inherits from null extension. I >> heard that there was an intention to add the kludge. Though semantically >> broken it would save programs like yours. > > That's actually an Ada 2005 feature - see 3.9.3(4/2). One that I was against > because it introduces a maintenance problem (null extensions tend to turn > into non-null extensions over time, and once that happens, the feature stops > working). Right. > It appears that GNAT failed to properly implement this feature in the case > described by the OP. But the extension is null only *privately*. How can a public declaration of the overriding depend on private declaration? It looks totally crazy to me. > OTOH, as soon as the OP starts adding components to the extension, they'll > have the problem again. (I don't think it is practical to extend the > predefined containers, particularly because it's impossible to derive a > cursor type to go with the extended container -- so you either end up with > broken typing or you have do everything twice. And there are a lot of > functions that have to be overridden.) Maintaining parallel types hierarchies is a big type system problem, which is unclear how to resolve. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: orthogonal inheritance and extension aggregates 2013-07-20 6:36 ` Dmitry A. Kazakov @ 2013-07-20 7:54 ` Niklas Holsti 0 siblings, 0 replies; 11+ messages in thread From: Niklas Holsti @ 2013-07-20 7:54 UTC (permalink / raw) On 13-07-20 09:36 , Dmitry A. Kazakov wrote: > On Sat, 20 Jul 2013 00:49:41 -0500, Randy Brukardt wrote: > >> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >> news:igo2b70i4ox.t5duix9weguc$.dlg@40tude.net... >> ... >>> P.S. I don't know if Ada 2012 silently inherits from null extension. I >>> heard that there was an intention to add the kludge. Though semantically >>> broken it would save programs like yours. >> >> That's actually an Ada 2005 feature - see 3.9.3(4/2). One that I was against >> because it introduces a maintenance problem (null extensions tend to turn >> into non-null extensions over time, and once that happens, the feature stops >> working). > > Right. > >> It appears that GNAT failed to properly implement this feature in the case >> described by the OP. > > But the extension is null only *privately*. The *private* extension looked non-null to me in the original post, where an ellipsis marks the extension in the private part of the generic: On 13-07-19 20:46 , Felix Krause wrote: > I try to implement some feature via orthogonal inhertance like this: > > generic > type Parent (<>) is abstract tagged private; > package Extension is > type Extended is abstract new Parent with private; > > -- operations of the extended type here > private > type Extended is abstract new Parent with record > -- … > end record; > end Extension; The visible extension to a non-abstract type was null: > type Child is new Base.Extended with null record; It seems to me that GNAT is working correctly on this code, assuming that the private extension is not null. -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ . ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2013-07-22 22:03 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2013-07-19 17:46 orthogonal inheritance and extension aggregates Felix Krause 2013-07-19 18:00 ` Adam Beneschan 2013-07-22 16:02 ` Adam Beneschan 2013-07-22 18:44 ` Dmitry A. Kazakov 2013-07-22 18:59 ` Adam Beneschan 2013-07-22 19:19 ` Adam Beneschan 2013-07-22 22:03 ` Felix Krause 2013-07-19 19:05 ` Dmitry A. Kazakov 2013-07-20 5:49 ` Randy Brukardt 2013-07-20 6:36 ` Dmitry A. Kazakov 2013-07-20 7:54 ` Niklas Holsti
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox