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.
     *)
    datatype GenericRecord =
	PR of PersonRecord |
	SE of StaffEmployee |
	P of Programmer |
	M of Manager

    (*
     * 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.
     *)
    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 newProgrammer(NameVal, AgeVal, AddressVal, SalaryVal, StepVal) =
	    Data{Key=NameVal, Data=
		P{Name=NameVal, Age=AgeVal, Address=AddressVal,
		    SpecializedInfo={Salary=SalaryVal, Step=StepVal,
			SpecializedInfo={Languages=["C", "ML"]}}}}
    (*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 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)
    end

end