<<

Records, Variants, and Pattern Matching Type abbreviations and definitions Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Overview

Elements of Programming Languages Lecture 7: Records, variants, and subtyping Last time: Simple data structures: pairing (product types), choice (sum types) James Cheney Today: Records (generalizing products), variants (generalizing University of Edinburgh sums) and pattern matching Subtyping October 12, 2017

Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records Named variants

Records generalize pairs to n-tuples with named fields. As mentioned earlier, named variants generalize binary e ::= l = e ,...,l = e e.l variants just as records generalize pairs ···|h1 1 n ni| v ::= l1 = v1,...,ln = vn e ::= C (e) case e of C (x) e ; ... ···|h i ···| i | { 1 ) 1 } ⌧ ::= l1 : ⌧1,...,ln : ⌧n v ::= C (v) ···|h i ···| i ⌧ ::= [C : ⌧ ,...,C : ⌧ ] Examples: ···| 1 1 n n fst=1, snd="forty-two" .snd "forty-two" Basic idea: allow a choice of n cases, each with a name h i 7! x=3.0, y=4.0, length=5.0 To construct a named variant, use the constructor name h i on a value of the appropriate type, e.g. Ci (ei )where Record fields can be (first-class) functions too: ei : ⌧i case x=3.0, y=4.0, length=(x, y). sqrt(x x + y y) The construct generalizes to named variants also h ⇤ ⇤ i Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Named variants in Scala: case classes Aside: Records and Variants in Haskell

We have already seen (and used) Scala’s case class mechanism In Haskell, data defines a recursive, named variant type abstract class IntList data IntList = Nil | Cons Int IntList case class Nil() extends IntList and cases can define named fields: case class Cons(head: Int, tail: IntList) data Point = Point {x :: Double, y :: Double} extends IntList In both cases the newly defined type is di↵erent from any Note: IntList, Nil, Cons are newly defined types, other type seen so far, and the named constructor(s) can di↵erent from any others. be used in pattern matching Case classes support pattern matching This approach dates to the ML programming language (Milner et al.) and earlier designs such as HOPE (Burstall def foo(x: IntList) = x match { et al.). case Nil() => ... (Both developed in Edinburgh) case Cons(head,tail) => ... }

Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Pattern matching More pattern matching

Datatypes and case classes support pattern matching Variables cannot be repeated, instead, explicit equality We have seen a simple form of pattern matching for sum tests need to be used. types. The special pattern _ matches anything This generalizes to named variants Patterns can overlap, and usually they are tried in order But still is very limited: we only consider one “level” at atime result match { case OK => println("All is well") Patterns typically also include constants and pairs/records case _ => println("Release the hounds!") x match { case (1, (true, "abcd")) => ...} } // not the same as Patterns in Scala, Haskell, ML can also be nested:that result match { is, they can match more than one constructor case _ => println("Release the hounds!") case OK => println("All is well") x match { case Cons(1,Cons(y,Nil())) => ...} } Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Expanding nested pattern matching Type abbreviations

Obviously, it quickly becomes painful to write Nested pattern matching can be expanded out: ” x : int, y : str ”overandover. h i l match { Type abbreviations introduce a name for a type. case Cons(x,Cons(y,Nil())) => ... type = ⌧ } An abbreviation name T treated the same as its expands to expansion ⌧ l match { (much like let-bound variables) case Cons(x,t1) => t1 match { Examples: case Cons(y,t2) => t2 match { type Point = x:dbl, y:dbl case Nil() => ... h i }}} type Point3d = x:dbl, y:dbl, z:dbl type Color = r:hint, g:int, b:int i type ColoredPointh = x:dbl, y:dbli, c:Color h i

Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Type definitions Type definitions vs. abbreviations in practice

Instead, can also consider defining new (named) types In Haskell, type abbreviations are introduced by type, deftype T = ⌧ while new types can be defined by data or newtype declarations. The term generative is sometimes used to refer to In Java, there is no explicit notation for type definitions that create a new entity rather than abbreviations; the only way to define a new type is to introducing an abbreviation define a class or Type abbreviations are usually not allowed to be In Scala, type abbreviations are introduced by type,while recursive; type definitions can be. the class, object and trait constructs define new types deftype IntList =[Nil : unit, Cons : int IntList] ⇥ Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Subtyping Subtyping

Liskov proposed a guideline for subtyping: Suppose we have a function: Liskov Substitution Principle dist = p:Point. sqrt((p.x)2 +(p.y)2) If S is a subtype of T ,thenobjectsoftypeT may be replaced with objects of type S without altering any of the desirable for computing the distance to the origin. properties of the program. Only the x and y fields are needed for this, so we’d like to be able to use this on ColoredPointsalso. If we use ⌧<: ⌧ 0 to mean “⌧ is a subtype of ⌧ 0”, and consider well-typedness to be desirable, then we can But, this doesn’t typecheck: translate this to the following subsumption rule: dist( x=8.0, y=12.0, c=purple )=13.0 e : ⌧ ⌧ <: ⌧ h i ` 1 1 2 e : ⌧2 We can introduce a subtyping relationship between Point ` and ColoredPoint to allow for this. This says: if e has type ⌧1 and ⌧1 <: ⌧2,thenwecan proceed by pretending it has type ⌧2.

Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Record subtyping: width and depth Examples

There are several di↵erent ways to define subtyping for (We’ll abbreviate P = Point, P3=Point3d, records. CP = ColoredPoint to save space...) Width subtyping: subtype has same fields as supertype So we have: (with identical types), and may have additional fields at the end: P3d = x:dbl, y:dbl, z:dbl <: x:dbl, y:dbl = P h i h i l : ⌧ ,...,l : ⌧ ,...,l : ⌧ <: l : ⌧ ,...,l : ⌧ h 1 1 n n n+k n+k i h 1 1 n ni CP = x:dbl, y:dbl, c:Color <: x:dbl, y:dbl = P h i h i Depth subtyping: subtype’s fields are pointwise but no other subtyping relationships hold subtypes of supertype So, we can call dist on Point3d or ColoredPoint: ⌧1 <: ⌧10 ⌧n <: ⌧n0 ··· . l1 : ⌧1,...,ln : ⌧n <: l1 : ⌧10 ,...,ln : ⌧n0 x : P3d x : P3dP3d <: P . h i h i x`: P3d x : P x : P3d dist : P dbl These rules can be combined. Optionally, field reordering ` x : P3d dist(x):dbl` ! can also be allowed (but is harder to implement). ` Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Subtyping for pairs and variants Subtyping for functions

When is A B <: A B ? 1 ! 1 2 ! 2 For pairs, subtyping is componentwise Maybe componentwise, like pairs?

⌧1 <: ⌧10 ⌧2 <: ⌧20 ⌧1 <: ⌧10 ⌧2 <: ⌧20 ⌧1 ⌧2 <: ⌧ 0 ⌧ 0 ⌧ ⌧ <: ⌧ 0 ⌧ 0 1 2 1 ⇥ 2 1 ⇥ 2 ! ! But then we can do this (where (p)=P): Similarly for binary variants CP <: PCP<: CP ⌧1 <: ⌧10 ⌧2 <: ⌧20 x.x : CP CP CP CP <: P CP ` ! ! ! ⌧1 + ⌧2 <: ⌧ 0 + ⌧ 0 x.x : P CP p : P 1 2 ` ! ` (x.x)p : CP For named variants, can have additional subtyping rules ` (but this is rare) So, once ColoredPoint is a subtype of Point,wecan change any Point to a ColoredPoint also. That doesn’t seem right.

Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Covariant vs. contravariant The “top” and “bottom” types

For the result type of a function (and for pairs and other data structures), the direction of subtyping is preserved: any:atypethatisasupertypeofalltypes. Such a type describes the common interface of all its ⌧2 <: ⌧ 0 2 subtypes (e.g. hashing, equality in Java) ⌧ ⌧ <: ⌧ ⌧ 0 1 ! 2 1 ! 2 In Scala, this is called Any Subtyping of function results, pairs, etc., where order is empty:atypethatisasubtypeofalltypes. preserved, is covariant. Usually, such a type is considered to be empty:there For the argument type of a function, the direction of cannot actually be any values of this type. subtyping is flipped: We’ve actually encountered this before, as the degenerate case of a choice type where there are zero ⌧10 <: ⌧1 chioces Nothing ⌧1 ⌧2 <: ⌧10 ⌧2 In Scala, this type is called .SoforanyScala ! ! type ⌧ we have Nothing <: ⌧<: Any. Subtyping of function arguments, where order is reversed, is called contravariant. Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Summary: Subtyping rules Structural vs. Nominal subtyping

⌧1 <: ⌧2 The approach to subtyping considered so far is called structural. ⌧1 <: ⌧2 ⌧2 <: ⌧3 The names we use for type abbreviations don’t matter, empty <: ⌧ ⌧<: any ⌧<: ⌧ ⌧ <: ⌧ 1 3 only their structure. For example, Point3d <: Point ⌧1 <: ⌧10 ⌧2 <: ⌧20 ⌧1 <: ⌧10 ⌧2 <: ⌧20 because Point3d has all of the fields of Point (and more). ⌧ ⌧ <: ⌧ 0 ⌧ 0 ⌧ + ⌧ <: ⌧ 0 + ⌧ 0 1 ⇥ 2 1 ⇥ 2 1 2 1 2 Then dist(p)alsorunsonp : Point3d (and gives a ⌧10 <: ⌧1 ⌧2 <: ⌧20 nonsense answer!) ⌧ ⌧ <: ⌧ 0 ⌧ 0 So far, a defined type has no subtypes (other than itself). 1 ! 2 1 ! 2 By default, definitions ColoredPoint, Point and Point3d Notice that we combine the covariant and contravariant rules are unrelated. for functions into a single rule.

Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Records, Variants, and Pattern Matching Type abbreviations and definitions Subtyping Structural vs. Nominal subtyping Summary

If we defined new types Point 0 and Point3d 0,ratherthan treating them as abbreviations, then we have more control over subtyping Then we can declare ColoredPoint0 to be a subtype of Today we covered: Point 0 Records, variants, and pattern matching deftype Point 0 = x:dbl, y:dbl Type abbreviations and definitions h i deftype ColoredPoint0 <: Point 0 = x:dbl, y:dbl, c:Color Subtyping h i Next time: However, we could choose not to assert Point3d 0 to be a Polymorphism and type inference subtype of Point 0,preventing(mis)useofsubtypingto view Point3d 0sasPoint 0s. This nominal subtyping is used in Java and Scala A defined type can only be a subtype of another if it is declared as such More on this later!