structure PersonDB = struct exception RecordNotFound (* * Record ia a parameterized type at the top of the class hierarchy. *) type 'x Record = { Name : string, Age : int, Address : string, SpecializedInfo: 'x } (* * A PersonRecord is subtype of Person with no specialization. *) type PersonRecord = {} Record (* * A StaffEmployee is a subtype of Record, with two added fields. *) type StaffEmployee = { HourlyWage: int, EmploymentStatus : int } Record (* * A SalariedEmpolyee is a subtype of Record, with two added fields. *) type 'x SalariedEmployee = { Salary: int, Step: int, SpecializedInfo: 'x } Record (* * A Programmer is a subtype of Salaried Employee (subsubtype of Person), * with one added field. *) type Programmer = { Languages: string list } SalariedEmployee (* * A Manager is a subtype of Salaried Employee (subsubtype of Person), with * one added field. *) type Manager = { Supervisees: string list } SalariedEmployee (* * A GenericRecord is the union of all record types that will be stored in * the database. Whenever a new subclass is added to the Record hierarchy, * its type must be "registered" in the GenericRecord union. *) datatype GenericRecord = PR of PersonRecord | SE of StaffEmployee | SaE of {} SalariedEmployee | P of Programmer | M of Manager (*with*) exception BadGenericRecord fun GetPR(PR(x)) = x | GetPR(x) = raise BadGenericRecord fun GetSE(SE(x)) = x | GetSE(x) = raise BadGenericRecord fun GetSaE(SaE(x)) = x | GetSaE(x) = raise BadGenericRecord fun GetP(P(x)) = x | GetP(x) = raise BadGenericRecord fun GetM(M(x)) = x | GetM(x) = raise BadGenericRecord (*end*) (* * A KeyedRecord is a record with an unbundled key, so that a generic * database find operation can be implemented. It's a datatype instead of * an abstype so that the find operation can access its constructors. * * We associate the constructors for each (sub)type of record with this * keyed record, since its what has all of the data packaged inside it. *) datatype KeyedRecord = EmptyRecord | Data of {Key: string, Data: GenericRecord} (*with*) fun NewPersonRecord(NameVal, AgeVal, AddressVal) = Data{Key=NameVal, Data= PR{Name=NameVal, Age=AgeVal, Address=AddressVal, SpecializedInfo={}}} fun NewStaffEmployee(NameVal, AgeVal, AddressVal, HourlyVal, ESVal) = Data{Key=NameVal, Data= SE{Name=NameVal, Age=AgeVal, Address=AddressVal, SpecializedInfo={HourlyWage=HourlyVal, EmploymentStatus=ESVal}}}; fun NewSalariedEmployee(NameVal, AgeVal, AddressVal, SalaryVal, StepVal) = Data{Key=NameVal, Data= SaE{Name=NameVal, Age=AgeVal, Address=AddressVal, SpecializedInfo={Salary=SalaryVal, Step=StepVal, SpecializedInfo={}}}} fun NewProgrammer(NameVal, AgeVal, AddressVal, SalaryVal, StepVal, LangsVal) = Data{Key=NameVal, Data= P{Name=NameVal, Age=AgeVal, Address=AddressVal, SpecializedInfo={Salary=SalaryVal, Step=StepVal, SpecializedInfo={Languages=LangsVal}}}} fun NewManager(NameVal, AgeVal, AddressVal, SalaryVal, StepVal, SuperviseesVal) = Data{Key=NameVal, Data= M{Name=NameVal, Age=AgeVal, Address=AddressVal, SpecializedInfo={Salary=SalaryVal, Step=StepVal, SpecializedInfo={Supervisees=SuperviseesVal}}}} (*end*) (* * PersonDatabase a is a generic database, intended to hold KeyedRecords. *) abstype 'p PersonDatabase = EmptyDB | Body of 'p list with val NewPersonDatabase = EmptyDB fun AddPerson(EmptyDB, p) = Body([p]) | AddPerson(Body(b), p) = Body(b @ [p]) fun UnBody(Body(b)) = b | UnBody(x) = nil fun DelPerson(EmptyDB, k) = EmptyDB | DelPerson(Body(Data((p as {Key=k, ...})) :: ps), k1) = if k = k1 then Body(ps) else Body(Data((p:{Key: string, Data: GenericRecord})) :: UnBody(DelPerson(Body(ps), k1))) | DelPerson(x) = EmptyDB fun FindPerson(EmptyDB, k) = raise RecordNotFound | FindPerson(Body(Data((p as {Key=k, ...})) :: ps), k1) = if k = k1 then #Data(p:{Key: string, Data: GenericRecord}) else FindPerson(Body(ps), k1) | FindPerson(x) = raise RecordNotFound end end