Generic polymorphism on steroids

Similar documents
Type Checking and Type Inference

From last time. News. Example. Example. Typechecking let. Typechecking let. Pick up your first two quizzes after class. Assignment #2 due tomorrow

Parametric types. map: ( a (a b) a list b list. filter: ( a bool) a list a list. Why? a and b are type variables!

CSCI-GA Scripting Languages

Goal. CS152: Programming Languages. Lecture 15 Parametric Polymorphism. What the Library Likes. What The Client Likes. Start simpler.

Overloading, Type Classes, and Algebraic Datatypes

Adding GADTs to OCaml the direct approach

Tracing Ambiguity in GADT Type Inference

CSE 130: Programming Languages. Polymorphism. Ranjit Jhala UC San Diego

Code reuse through polymorphic variants

Programming Languages Lecture 15: Recursive Types & Subtyping

Programming Languages Lecture 14: Sum, Product, Recursive Types

Type assignment for intersections and unions in call-by-value languages

Back to OCaml. Summary of polymorphism. Example 1. Type inference. Example 2. Example 2. Subtype. Polymorphic types allow us to reuse code.

Parametric types. aka: what s up with those a???

Structural polymorphism in Generic Haskell

CSE-505: Programming Languages. Lecture 20.5 Recursive Types. Zach Tatlock 2016

TYPE INFERENCE. François Pottier. The Programming Languages Mentoring ICFP August 30, 2015

CS558 Programming Languages

Part III. Chapter 15: Subtyping

CS558 Programming Languages

CS-XXX: Graduate Programming Languages. Lecture 17 Recursive Types. Dan Grossman 2012

GADTs meet Subtyping

CS558 Programming Languages

l e t print_name r = p r i n t _ e n d l i n e ( Name : ^ r. name)

Overview. Elements of Programming Languages. Another example. Consider the humble identity function

CMSC 330: Organization of Programming Languages

Harvard School of Engineering and Applied Sciences Computer Science 152

Polymorphism and Type Inference

CVO103: Programming Languages. Lecture 5 Design and Implementation of PLs (1) Expressions

Topic 16: Issues in compiling functional and object-oriented languages

Polymorphic lambda calculus Princ. of Progr. Languages (and Extended ) The University of Birmingham. c Uday Reddy

Why Types Matter. Zheng Gao CREST, UCL

Typed Racket: Racket with Static Types

Polymorphism and Type Inference

GADTs gone mild. Code at Gabriel RADANNE 23 Mars 2017

Types and Programming Languages. Lecture 5. Extensions of simple types

Typed Scheme: Scheme with Static Types

Agenda. CS301 Session 11. Common type constructors. Things we could add to Impcore. Discussion: midterm exam - take-home or inclass?

Part III Chapter 15: Subtyping

Lecture #23: Conversion and Type Inference

Ornaments in ML. Thomas Williams, Didier Rémy. April 18, Inria - Gallium

Conversion vs. Subtyping. Lecture #23: Conversion and Type Inference. Integer Conversions. Conversions: Implicit vs. Explicit. Object x = "Hello";

The type checker will complain that the two branches have different types, one is string and the other is int

CS-XXX: Graduate Programming Languages. Lecture 22 Class-Based Object-Oriented Programming. Dan Grossman 2012

Programming Languages and Compilers (CS 421)

Topics Covered Thus Far. CMSC 330: Organization of Programming Languages. Language Features Covered Thus Far. Programming Languages Revisited

OCaml. History, Variants. ML s holy trinity. Interacting with ML. Base type: Integers. Base type: Strings. *Notes from Sorin Lerner at UCSD*

COSE212: Programming Languages. Lecture 3 Functional Programming in OCaml

Plan (next 4 weeks) 1. Fast forward. 2. Rewind. 3. Slow motion. Rapid introduction to what s in OCaml. Go over the pieces individually

Topics Covered Thus Far CMSC 330: Organization of Programming Languages

CS153: Compilers Lecture 14: Type Checking

The story so far. Elements of Programming Languages. Pairs in various languages. Pairs

Where is ML type inference headed?

Introduction to Typed Racket. The plan: Racket Crash Course Typed Racket and PL Racket Differences with the text Some PL Racket Examples

CSE-321: Assignment 8 (100 points)

Cunning Plan. One-Slide Summary. Functional Programming. Functional Programming. Introduction to COOL #1. Classroom Object-Oriented Language

Introduction to OCaml

Programming in Standard ML: Continued

OCaml. ML Flow. Complex types: Lists. Complex types: Lists. The PL for the discerning hacker. All elements must have same type.

Data Abstraction. An Abstraction for Inductive Data Types. Philip W. L. Fong.

Semantic Processing (Part 2)

The Typed Racket Guide

Pierce Ch. 3, 8, 11, 15. Type Systems

CSE 130, Fall 2006: Final Examination

Some instance messages and methods

Functional programming Primer I

L3 Programming September 19, OCaml Cheatsheet

An update on XML types

Modular implicits for OCaml how to assert success. Gallium Seminar,

CSCC24 Functional Programming Typing, Scope, Exceptions ML

Subsumption. Principle of safe substitution

Variables. Substitution

Compiler construction

n n Try tutorial on front page to get started! n spring13/ n Stack Overflow!

Programming Languages

CSE 130 Programming Languages. Lecture 3: Datatypes. Ranjit Jhala UC San Diego

Programming in Omega Part 1. Tim Sheard Portland State University

CSE Lecture 7: Polymorphism and generics 16 September Nate Nystrom UTA

Any questions. Say hello to OCaml. Say hello to OCaml. Why readability matters. History, Variants. Plan (next 4 weeks)

Advances in Programming Languages

Types. Type checking. Why Do We Need Type Systems? Types and Operations. What is a type? Consensus

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

Polymorphism and System-F (OV)

Recap: Functions as first-class values

10/18/18. Outline. Semantic Analysis. Two types of semantic rules. Syntax vs. Semantics. Static Semantics. Static Semantics.

Homework 3 COSE212, Fall 2018

Calculus of Inductive Constructions

Recap: ML s Holy Trinity. Story So Far... CSE 130 Programming Languages. Datatypes. A function is a value! Next: functions, but remember.

CSE 130 Programming Languages. Datatypes. Ranjit Jhala UC San Diego

An introduction introduction to functional functional programming programming using usin Haskell

The Typed Racket Reference

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

CMSC 330: Organization of Programming Languages

Reasoning About Programs Panagiotis Manolios

Programming Languages

Questions? Static Semantics. Static Semantics. Static Semantics. Next week on Wednesday (5 th of October) no

ML Type Inference and Unification. Arlen Cox

Types, Type Inference and Unification

Typed Recursion {with {mk-rec : (((num -> num) -> (num -> num)) -> (num -> num)) {fun {body : ((num -> num) -> (num -> num))} {{fun {fx :

Transcription:

Generic polymorphism on steroids or How to Solve the Expression Problem with Polymorphic Variants Claudio Sacerdoti Coen <claudio.sacerdoticoen@unibo.it> Dipartimento di Informatica Scienza e Ingegneria Alma mater studiorum Università di Bologna 28 March 2015 1 / 52

Content of the talk The Expression Problem An archetipal recurring problem in programming. Widely used to justify object oriented programming. Very hard for most non object oriented functional languages Polymorphic Variants A simple and natural programming language construct Elegantly solves the expression problem The solution is better than the OO one Requires and pushes to its limits generic polymorphism Available in OCaml only 2 / 52

Content of the talk The Expression Problem An archetipal recurring problem in programming. Widely used to justify object oriented programming. Very hard for most non object oriented functional languages Polymorphic Variants A simple and natural programming language construct Elegantly solves the expression problem The solution is better than the OO one Requires and pushes to its limits generic polymorphism Available in OCaml only 3 / 52

Outline 1 Preliminaries 2 The Expression Problem 3 Polymorphic Variants 4 The Expression Problem with Polymorphic Variants 5 Conclusions 4 / 52

Outline 1 Preliminaries 2 The Expression Problem 3 Polymorphic Variants 4 The Expression Problem with Polymorphic Variants 5 Conclusions 5 / 52

Principle of Uniformity Every datum that can be passed to a function returned by a function stored in a variable or data structure has the same constant size (tipically one or two words). In OCaml: primitive data are all stored in a word complex data are manipulated by reference (again a word) 6 / 52

Principle of Uniformity Every datum that can be passed to a function returned by a function stored in a variable or data structure has the same constant size (tipically one or two words). In OCaml: primitive data are all stored in a word complex data are manipulated by reference (again a word) 7 / 52

Generic (or parametric) polymorphism Code that manipulates values without using them works identically on values belonging to different data types. The types of generic parameters are universally quantified at the definition site, and instantiated at usage site. let swap (x,y) = (y,x) ( val swap: α, β. α β β α ) ( val swap: a b b a ) let n,m = swap (2,3) ( a int, b int ) let n,s = swap ( Foo,0) ( a string, b int ) Sufficient condition: principle of uniformity 8 / 52

Generic (or parametric) polymorphism Code that manipulates values without using them works identically on values belonging to different data types. The types of generic parameters are universally quantified at the definition site, and instantiated at usage site. let swap (x,y) = (y,x) ( val swap: α, β. α β β α ) ( val swap: a b b a ) let n,m = swap (2,3) ( a int, b int ) let n,s = swap ( Foo,0) ( a string, b int ) Sufficient condition: principle of uniformity 9 / 52

Type inference: the user writes untyped code the system infers the most general type Type Inference and ML ( val f: a int ( a b) b int ) let f (x,y,z) = if x=fst z then (snd z,y) else (snd z,y+1) General case: undecidable. Hindley-Milner (ML): universal quantifiers only in prenex position type checking becomes decidable and efficient in practice 10 / 52

Type inference: the user writes untyped code the system infers the most general type Type Inference and ML ( val f: a int ( a b) b int ) let f (x,y,z) = if x=fst z then (snd z,y) else (snd z,y+1) General case: undecidable. Hindley-Milner (ML): universal quantifiers only in prenex position type checking becomes decidable and efficient in practice 11 / 52

Benefits Parametric polymorphism + type inference: + Extremely concise code + Improved reusability + Resilience to changes in datatypes - (Rarely) hard to understand typing errors Types are assigned by globally analyzing the usage of data. Errors are reported locally: x has type S but it is expected to have type T S may be correct, and the error is elsewhere S and T may be extremely large expressions 12 / 52

Benefits Parametric polymorphism + type inference: + Extremely concise code + Improved reusability + Resilience to changes in datatypes - (Rarely) hard to understand typing errors Types are assigned by globally analyzing the usage of data. Errors are reported locally: x has type S but it is expected to have type T S may be correct, and the error is elsewhere S and T may be extremely large expressions 13 / 52

Algebraic Data Types ADT = recursive disjoint labelled union of products of types. type a list = Nil Cons of a a list type a tree = Leaf of a Node of a tree a a tree type expr = Var of string Const of int Plus of expr expr let x = Cons (1, Cons (2, Nil )) ( The list [1;2] ) let e = Plus (Var x, Const 2) ( The expression x+2 ) Pattern matching: let rec len x = match x with Nil 0 Cons (,y) 1 + len y Coverage check: all cases must be considered exactly once. 14 / 52

Algebraic Data Types ADT = recursive disjoint labelled union of products of types. type a list = Nil Cons of a a list type a tree = Leaf of a Node of a tree a a tree type expr = Var of string Const of int Plus of expr expr let x = Cons (1, Cons (2, Nil )) ( The list [1;2] ) let e = Plus (Var x, Const 2) ( The expression x+2 ) Pattern matching: let rec len x = match x with Nil 0 Cons (,y) 1 + len y Coverage check: all cases must be considered exactly once. 15 / 52

Algebraic Data Types ADT = recursive disjoint labelled union of products of types. type a list = Nil Cons of a a list type a tree = Leaf of a Node of a tree a a tree type expr = Var of string Const of int Plus of expr expr let x = Cons (1, Cons (2, Nil )) ( The list [1;2] ) let e = Plus (Var x, Const 2) ( The expression x+2 ) Pattern matching: let rec len x = match x with Nil 0 Cons (,y) 1 + len y Coverage check: all cases must be considered exactly once. 16 / 52

Nominal vs Structural typing Structural typing: two types are equal iff they expand to equal expressions Nominal typing: one type is only equal to itself (the name/location of the declaration matters) Mixed in ML: ( type abbreviations : t1 is equal to t2 ) type t1 = int string type t2 = int string ( type declarations : list1 is incompatible with list2 ) type a list1 = Nil Cons of a a list1 type a list2 = Nil Cons of a a list2 17 / 52

Nominal vs Structural typing Structural typing: two types are equal iff they expand to equal expressions Nominal typing: one type is only equal to itself (the name/location of the declaration matters) Mixed in ML: ( type abbreviations : t1 is equal to t2 ) type t1 = int string type t2 = int string ( type declarations : list1 is incompatible with list2 ) type a list1 = Nil Cons of a a list1 type a list2 = Nil Cons of a a list2 18 / 52

Outline 1 Preliminaries 2 The Expression Problem 3 Polymorphic Variants 4 The Expression Problem with Polymorphic Variants 5 Conclusions 19 / 52

Why the Expression Problem? An archetipal recurring problem in programming. Widely used to justify object oriented programming. Very hard for most non object oriented functional languages The solution with object oriented programming: Uses: Inheritance Subtype polymorphism Dynamic dispatch Open recursion (via self) Does not use: Incapsulation We will do without all of the above! 20 / 52

Why the Expression Problem? An archetipal recurring problem in programming. Widely used to justify object oriented programming. Very hard for most non object oriented functional languages The solution with object oriented programming: Uses: Inheritance Subtype polymorphism Dynamic dispatch Open recursion (via self) Does not use: Incapsulation We will do without all of the above! 21 / 52

Why the Expression Problem? An archetipal recurring problem in programming. Widely used to justify object oriented programming. Very hard for most non object oriented functional languages The solution with object oriented programming: Uses: Inheritance Subtype polymorphism Dynamic dispatch Open recursion (via self) Does not use: Incapsulation We will do without all of the above! 22 / 52

The Expression Problem Two problems to be solved reusing existent code without in place modification or cut&paste 1 Given a data type for expressions, and functions on them Add new functions Add new constructors 2 Given two data types for expressions, and functions on them Define the non disjoint union type e = Const of int Plus of e e type e = Mult of e e let eval e = let eval e = match e with match e with Const n n Mult(e1,e2) Plus(e1,e2) eval e1 + eval e2 eval e1 eval e2 23 / 52

interface E { int Eval (); } class Const implements E { public int n; Const(int n) { this.n = n; } public int Eval() { return n; } } The Expression Problem in Java class Plus implements E { public E l, r ; Plus(E l, E r) { this. l = l; this. r = r; } public int Eval() { return l.eval() + r.eval (); } } Too verbose and obfuscated. Too open: what if I want to restrict to certain expressions? 24 / 52

interface E { int Eval (); } class Const implements E { public int n; Const(int n) { this.n = n; } public int Eval() { return n; } } The Expression Problem in Java class Plus implements E { public E l, r ; Plus(E l, E r) { this. l = l; this. r = r; } public int Eval() { return l.eval() + r.eval (); } } Too verbose and obfuscated. Too open: what if I want to restrict to certain expressions? 25 / 52

The Expression Problem in ML type e = Const of int Plus of e e type e = Mult of e e let eval e = let eval e = match e with match e with Const n n Mult(e1,e2) Plus(e1,e2) eval e1 + eval e2 eval e1 eval e2 type e = Const of int Plus of e e Mult of e e let eval e = match e with Const n n Plus(e1,e2) eval e1 + eval e2 Mult(e1,e2) eval e1 eval e2 Cut & paste no code reused 26 / 52

The Expression Problem in ML type e = Const of int Plus of e e type e = Mult of e e let eval e = let eval e = match e with match e with Const n n Mult(e1,e2) Plus(e1,e2) eval e1 + eval e2 eval e1 eval e2 type e = Const of int Plus of e e Mult of e e let eval e = match e with Const n n Plus(e1,e2) eval e1 + eval e2 Mult(e1,e2) eval e1 eval e2 Cut & paste no code reused 27 / 52

First problem: closed recursion Opening the recursion: type a e = Cons of int Plus of a a The Expression Problem in ML ( val eval: ( a int) a e int ) let eval f x = match x with Cons n n Plus(e1,e2) f e1 + f e2 Coverage still checked Open recursion can be closed at any time: type e1 = e1 e No unwanted elements ( val eval1: e1 int ) let rec eval1 x = eval eval1 x 28 / 52

First problem: closed recursion Opening the recursion: type a e = Cons of int Plus of a a The Expression Problem in ML ( val eval: ( a int) a e int ) let eval f x = match x with Cons n n Plus(e1,e2) f e1 + f e2 Coverage still checked Open recursion can be closed at any time: type e1 = e1 e No unwanted elements ( val eval1: e1 int ) let rec eval1 x = eval eval1 x 29 / 52

First problem: closed recursion Opening the recursion: type a e = Cons of int Plus of a a The Expression Problem in ML ( val eval: ( a int) a e int ) let eval f x = match x with Cons n n Plus(e1,e2) f e1 + f e2 Coverage still checked Open recursion can be closed at any time: type e1 = e1 e No unwanted elements ( val eval1: e1 int ) let rec eval1 x = eval eval1 x 30 / 52

Problems: Closed recursion: fixed The Expression Problem in ML Lack of extensibility: an ADT fixes once and for all the constructors How to add a constructor to type a e = Cons of int Plus of a a without cut&paste? No solution in ML. Approximated solution, only for first formulation: type ( a, b) e = Cons of int Plus of a a More of b Needs an empty type to close the recursion (not in ML). Symmetrically merging two datatypes is impossible. 31 / 52

Problems: Closed recursion: fixed The Expression Problem in ML Lack of extensibility: an ADT fixes once and for all the constructors How to add a constructor to type a e = Cons of int Plus of a a without cut&paste? No solution in ML. Approximated solution, only for first formulation: type ( a, b) e = Cons of int Plus of a a More of b Needs an empty type to close the recursion (not in ML). Symmetrically merging two datatypes is impossible. 32 / 52

Outline 1 Preliminaries 2 The Expression Problem 3 Polymorphic Variants 4 The Expression Problem with Polymorphic Variants 5 Conclusions 33 / 52

Polymorphic variants Decompose ADT into two constructions: 1 Labelling of products of types 2 Non disjoint union of types ADTs up to structural equality can be recovered. ( val x : [> A of string int ] ) let x = A ( foo,2) ( val m : [> A of string int B of int C] list ) let m = [ A ( foo,2) ; B 4 ; C ] 34 / 52

Polymorphic variants Decompose ADT into two constructions: 1 Labelling of products of types 2 Non disjoint union of types ADTs up to structural equality can be recovered. ( val x : [> A of string int ] ) let x = A ( foo,2) ( val m : [> A of string int B of int C] list ) let m = [ A ( foo,2) ; B 4 ; C ] 35 / 52

Typing Polymorphic Variants ( val x : [> A of string int ] ) let x = A ( foo,2) [> A of string int ] is the most general type for A ( foo,2) How to read it? It is of the form α. [ A of string int α] for any union of tagged types α + It is of the form α.α such that 1 α is a union of tagged types 2 α includes at least [ A of string int] (cfr. bounded polymorphism) - It is of the form α.α for α being a subtype of [ A of string int] (no: implicit subtyping violates principal typing) 36 / 52

Typing Polymorphic Variants It is of the form α. [ A of string int α] for any union of tagged types α ( val x : [> A of string int ] Generic type ) let x = A of ( foo,2) ( val y : [ A of string int B] α [ B] ) let y = (x : [ A of string int B]) WARNING: in OCaml (t : T) is not a cast. It is a type instantiation operation: It checks if T is an instance of the generic type of t It behaves as t It is typed as T 37 / 52

Typing Polymorphic Variants It is of the form α.α such that 1 α is a union of tagged types 2 α includes at least [ A of string int] (cfr. bounded polymorphism) ( val x : [> A of string int ] Generic type ) let x = A of ( foo,2) ( val y : [ A of string int B] α [ A of string int B] ) let y = (x : [ A of string int B]) 38 / 52

Generic Polymorphism on Steroids ( val f : [< A of int a B of b C ] [> Ko Ok of int ] ) let f x = match x with A (n,m) Ok (n+1) B n Ok 0 C Ko Inferred type: α, β. α β such that α has at most A, B, C (of the given types) β has at least Ok, Ko (of the given types) Coverage check inference of most generic type 39 / 52

Generic Polymorphism on Steroids ( val f : [< A of int a B of b C ] [> Ko Ok of int ] ) let f x = match x with A (n,m) Ok (n+1) B n Ok 0 C Ko Inferred type: α, β. α β such that α has at most A, B, C (of the given types) β has at least Ok, Ko (of the given types) Coverage check inference of most generic type 40 / 52

Generic Polymorphism on Steroids ( val f : ([> Chihuaua Dog Maltese ] as a) a ) let rec f x = match x with Chihuaua Dog Maltese Dog y y Inferred type: α.α α such that α has at least Chihuaua, Dog, Maltese. 41 / 52

Generic Polymorphism on Steroids ( val f: [> A of int B ] int ) ( val g: [> A of int C ] bool ) ( val h: [> A of int B C ] int bool ) let h x = (f x, g x) [> A of int B C] α. [ A of int B C α] is the most general instance of both [> A of int B ] β. [ A of int B β] (by β [ C α]) [> A of int C ] γ. [ A of int C γ] (by γ [ B α]) The non disjoint union possible because the type of A was the same. 42 / 52

Generic Polymorphism on Steroids ( val f: [> A of int B ] int ) ( val g: [> A of int C ] bool ) ( val h: [> A of int B C ] int bool ) let h x = (f x, g x) [> A of int B C] α. [ A of int B C α] is the most general instance of both [> A of int B ] β. [ A of int B β] (by β [ C α]) [> A of int C ] γ. [ A of int C γ] (by γ [ B α]) The non disjoint union possible because the type of A was the same. 43 / 52

Outline 1 Preliminaries 2 The Expression Problem 3 Polymorphic Variants 4 The Expression Problem with Polymorphic Variants 5 Conclusions 44 / 52

The Expression Problem Solved Problems: Closed recursion: fixed (close later) Lack of extensibility: fixed (polymorphic variants) 45 / 52

The Expression Problem Solved type a e = [ Cons of int Plus of a a ] ( val eval e: ( a int) [< a e] int ) let eval e h x = match x with Cons n n Plus(e1,e2) h e1 + h e2 type a f = [ Mult of a a ] ( val eval f: ( a int) [< a f] int ) let eval f h x = match x with Mult(e1,e2) h e1 + h e2 ( The non disjoint union of e and f ) type a ef = [ a e a f ] #t matches all constructors of t ( val eval ef: ( a int) [< a ef ] int ) let eval ef f x = match x with #e as y eval e f y #f as y eval f f y 46 / 52

The Expression Problem Solved ( The non disjoint union of e and f ) type a ef = [ a e a f ] #t matches all constructors of t ( val eval ef: ( a int) [< a ef ] int ) let eval ef f x = match x with #e as y eval e f y #f as y eval f f y Open recursion can be closed at any time: ( The only inhabitants are Con, Plus, Mult ) type expr = expr ef ( val eval: [< expr] int ) let rec eval x = eval ef eval x 47 / 52

The Expression Problem Solved ( The non disjoint union of e and f ) type a ef = [ a e a f ] #t matches all constructors of t ( val eval ef: ( a int) [< a ef ] int ) let eval ef f x = match x with #e as y eval e f y #f as y eval f f y Open recursion can be closed at any time: ( The only inhabitants are Con, Plus, Mult ) type expr = expr ef ( val eval: [< expr] int ) let rec eval x = eval ef eval x 48 / 52

Outline 1 Preliminaries 2 The Expression Problem 3 Polymorphic Variants 4 The Expression Problem with Polymorphic Variants 5 Conclusions 49 / 52

Conclusions Polymorphic variants decompose ADT naturally: 1 Tagged values 2 Non disjoint unions Polymorphic Variants + Generic Polymorphism + Type Inference + Nominal Typing rocks! Who needs Inheritance, Subtype Polymorphism, Dynamic Dispatch and Open Recursion? No inheritance/subtyping most general types, type inference Static dispatch more efficiency Closed recursion static checks, coverage Much simpler semantics, easier to implement 50 / 52

What about duality? Polymorphic variants = non disjoint union of tagged values + structural typing + generic polymorphism and type inference Extensible records = duplicate-merging products of tagged values (fields) + structural typing + generic polymorphism and type inference ( val stats : Similar to objects {> height : int & width : int} but much simpler {< perimeter : int & area : int} ) let stats { height=h ; width=w } = { perimeter=2 (h+w) ; area=h w } 51 / 52

What about duality? Polymorphic variants = non disjoint union of tagged values + structural typing + generic polymorphism and type inference Extensible records = duplicate-merging products of tagged values (fields) + structural typing + generic polymorphism and type inference ( val stats : Similar to objects {> height : int & width : int} but much simpler {< perimeter : int & area : int} ) let stats { height=h ; width=w } = { perimeter=2 (h+w) ; area=h w } 52 / 52