(* * Example of a generic database and subclasses thereof. * * Random Notes: * * 0. Inheritance entails the following (this is now in the ref man): * a. The components of the parent class are automatically copied into * the components of the subclass. Any new components specified in * the subclass are anded onto the parent class components. I.e., if * the components expression appearing in the parent is E and the * components expression appearing in the subclass is E', then the * component expression of the subclass is defined as E and E'. * b. Any explicitly specified operations and equations are automatically * copied into the subclass, with all occurances of the parent class * name substituted with the subclass name. This leads to the notion * of an "inherited operation", the semantics of which are defined * shortly. * c. No other parent attributes are inherited. * d. An inherited operation need not be explicitly defined. If it is, * its signature must match exactly the signature of the operation * generated via the inheritance mechanism, and this explicitly * defined operation will be deemed a "defined inherited operation". * The operation from which it was derived will be deemed its "parent * operation". * e. The pre/post condtions of a parent operation are automatically * copied into the pre/post conditions, respectively, of the defined * inherited operation. Any new pre/post conditions specified in the * defined inherited operation are anded onto the parent operation * pre/post conditions. I.e., if the pre/post conditions appearing in * the parent are P/Q and the pre/post conditions appearing in the * defined inherited opeartion are P'/Q', then the pre/post conditions * of the defined inherited operation are defined as P and P'/Q and * Q'. * * 1. There is no need to specficy an ops attr if no eqns or subclasses are * defined. * * 2. The formal meaning of a failed precond is that the op returns empty * (which is the empty of the return type). * * 3. Classes are formally (internally) represented as unions. See further * notes elsewhere for discussion. *) obj GenericDB = GenericRecord* ops: AddRecord(GenericDB,GenericRecord)->(GenericDB); DelRecord(GenericDB,GenericKey)->(GenericDB); UpdateRecord(GenericDB,GenericRecord)->(GenericDB); FindRecord(GenericDB,GenericKey)->(GenericRecord); end; obj GenericRecord = key:GenericKey ops: FindRecord(GenericDB,GenericKey)->(GenericRecord); description: (* Specifying FindRecord explicitly here avoids the typical inheritance problem of type mismatch in the coarity of selectors. This is the case due to the copy-and-substitute semantics of inheritance for operations. *); end; obj GenericKey ops: DelRecord(GenericDB,GenericKey)->(GenericDB); DelRecord(GenericDB,GenericKey)->(GenericDB); FindRecord(GenericDB,GenericKey)->(GenericRecord); end; op AddRecord(gdb:GenericDB, gr:GenericRecord)->(gdb':GenericDB) pre: FindRecord(gdb, gr.key) = empty; post: FindRecord(gdb', gr.key) = gr; end; op DelRecord(gdb:GenericDB, key:GenericKey)->(gdb':GenericDB) pre: FindRecord(gdb, key) != empty; post: FindRecord(gdb, n) = empty; end; op UpdateRecord(gdb:GenericDB, gr:GenericRecord)->(gdb':GenericDB) pre: FindRecord(gdb, gr.key) != empty; post: FindRecord(gdb', gr.key) = gr; end; op FindRecord(gdb:GenericDB, key:GenericKey)->(gr':GenericRecord) pre: exists (gr:GenericRecord) (gr in gbd) and (gr.key = key); post: (gr' in gdb) and (gr'.key = key); end; (* * The following operations are automatically defined for types GenericDB and * GenericRecord. Further, the keyword empty universally denotes an invocation * of any such empty operation. *) op EmptyGenericDB()->(gdb':GenericDB) pre: ; post: forall (gr:GenericRecord) not (gr in gdb'); end; op EmptyGenericRecord()->(gr':GenericRecord) pre: ; post: gr'.key = empty; end; (* * Here is some subclassing. *) obj UserDB extends GenericDB description: (* Inherits all GenericDB ops, which means that new overloaded ops are automatically created with systematic signature substitution that replaces all occurances of "GenericDB" with "UserDB". Viz., the following ops are automatically defined: AddRecord(gdb:UserDB,gr:GenericRecord)->(UserDB) pre: FindRecord(gdb, gr.key) = empty; post: FindRecord(gdb', gr.key) = gr; DelRecord(UserDB,GenericKey)->(UserDB) UpdateRecord(UserDB,GenericRecord)->(UserDB) FindRecord(gdb:UserDB,key:GenericKey)->(gr':GenericRecord) pre: exists (gr:GenericRecord) (gr in gbd) and (gr.key = key); post: (gr' in gdb) and (gr'.key = key); *); end; obj UserRecord extends GenericRecord = Name, Id, Age, Sex, Address description: (* Creates the following op: FindRecord(gdb:GenericDB,key:GenericKey)->(gr':UserRecord) pre: exists (gr:UserRecord) (gr in gbd) and (gr.key = key); post: (gr' in gdb) and (gr'.key = key); WHICH MAY BE BOGUS. *); end; obj OtherDB extends GenericDB; obj OtherRecord extends GenericRecord = o:OtherField; obj OtherField; op BreakTypeSystem(odb:OtherDB, key:Key)->(ur:UserRecord) = FindRecord(odb, key); (* * Note well the following: There is no way to associate the Name or any other * field of the specialized UserRecord with the Generic Key. If one wanted to * use Name as this key field, then it would have to be left out of the * UserRecord specialization. *)