PPP Exercises: Subtyping and Contravariance


Exercise:

  1. Define a function 'printPerson' that displays persons. Use this representation for persons:
    Let Person = 
       Tuple
          name :String
          age :Int
       end;
    
  2. Define a function 'printPersonList' that displays a list of persons using the already defined function 'printPerson'. Use either your polymorphic list tuple considered in the exercise 'Parametric Polymorphism' or the module 'list' (see files 'bulkenv/List.ti' and 'bulkenv/list.tm' for included functions).

  3. The following type 'Student' is a subtype of 'Person', meaning that a value of type 'Student' may be used wherever a value of type 'Person' is valid. Define the functions 'printStudent' and 'printStudentList'. Use the already defined 'printPerson' as far as possible.
    Let Student = 
       Tuple
          name :String
          age :Int
          term :Int 
       end;
    
  4. Change the implementation of 'printPerson' so that it returns the printed person as result:
    let printPerson(p :Person) :Person = begin ... p end;
    
    Can we now define 'printStudent' as following?
         
    let printTerm(term :Int) = 
       print.string("term  " <> fmt.int(term));
    
    let printStudent(s :Student) =
       begin
           printTerm(printPerson(s).term)
           print.ln()
       end;
    
    What further modification in 'printPerson' would be required and why?

  5. Consider the following functions:
    let printPerson(p :Person) :Person = begin ... p end;
    let printStudent(s :Student) :Student = begin ... s end;
    
    Now consider a person and a student:
    let p :Person = tuple ... end;
    let s :Student = tuple ... end;
    
    and the following four functions:
    let a(p :Person  print(:Person) :Person) :Person = print(p);
    let b(p :Person  print(:Person) :Student) :Student = print(p);
    let c(s :Student  print(:Student) :Person) :Person = print(s);
    let d(s :Student  print(:Student) :Student) :Student = print(s);
    
    Can you tell which of the possible 16 combinations between these functions having 'p' or 's' as first parameter and 'printPerson' or 'printStudent' as second parameter are valid with respect to Tycoon subtyping rules and what are the reasons of errors (in case of multiple errors, consider the first)?

       
    				STATUS		REASON
    a(p printPerson)			
    
    a(p printStudent)	
     
    a(s printPerson)	
    
    a(s printStudent)	
    
    b(p printPerson)	
    
    b(p printStudent)               
    
    b(s printPerson)							        
    
    b(s printStudent)               
           
    c(p printPerson)		
    
    c(p printStudent)	
    
    c(s printPerson)		
    
    c(s printStudent)		
     
    d(p printPerson)	
    
    d(p printStudent)
    
    d(s printPerson)
    
    d(s printStudent)
    
To bring back to your mind the concept of contravariance here is a short explanation:
Imagine two types, e.g. Person and Student. One is subtype of the other: Student <: Person, which means that a value of type Student can be used wherever a value of type Person is needed.
Now imagine two functions, e.g. hirePerson(:Person) :Ok and hireStudent(:Student) :Ok. Could you state that hireStudent is subtype of hirePerson?
No, you couldn't, as hireStudent expects a student as parameter. Student might have more attributes than person. If you went ahead and used hireStudent somewhere instead of hirePerson, it would possibly get a person as parameter and the extra attributes of student would be missed. This is why the subtyping rule here works just the other way around: hirePerson <: hireStudent
So, that's what you call contravariance.


Ulrike Steffens, 1994