Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Product Types: From ML to Modern Languages

Hello! Welcome to the first lesson in our course on programming theory.

Given your background, we'll be able to dive deep into the formal underpinnings of these concepts, connecting them to the mathematical and logical principles you're familiar with. Your interest in John C. Mitchell's work is excellent, as his contributions are central to the formal understanding of modern programming languages.

This first module focuses on the Foundations of Functional Programming. Today, we'll begin with a cornerstone of typed functional languages: algebraic data types. Specifically, our goal is to define product types using records or tuples, tracing their formalization from the ML language family.

We will start with concrete examples in OCaml (a prominent member of the ML family) to build intuition, then generalize to the abstract concept of a "product type," and finally, examine how this concept was formally captured in the theoretical work that underpins these languages.

1. The Building Blocks: Tuples and Records

In programming, we constantly need to group related data together. A point has an x-coordinate and a y-coordinate. A student has a name and an ID number. Product types are the primary way typed functional languages achieve this. The two most common forms are tuples and records.

Let's watch a short video that introduces both in OCaml.

Records and Tuples | OCaml Programming | Chapter 3 Video 3

This video from Michael Ryan Clarkson's OCaml series provides a clear, practical introduction to records and tuples.

Please watch the following segments: Defining Record Types (00:00 - 01:39): Focus on how a student type is defined with named fields (name, grad_year) and how a value of this type is created. Defining Tuple Types (03:55 - 06:05): Observe how tuples group data positionally, without names (e.g., int * int * string). Accessing Tuple Components (06:05 - 07:30): Note the use of functions like fst and snd to get data out of a two-element tuple (a pair).

As you saw, the key distinction is:

  • Records group data using named fields (e.g., rbg.name). This is analogous to a struct in C, a dataclass in Python, or an object with properties in JavaScript.
  • Tuples group data using position (e.g., fst(p)). The components are anonymous and identified by their order.

A record is often just a more developer-friendly version of a tuple. The compiler can treat a record type student = {name: string; grad_year: int} as an underlying string * int tuple, where student.name is simply syntactic sugar for "get the first element."

2. From Concrete to Abstract: What is a "Product" Type?

Why are these called "product" types? The name comes from discrete mathematics and set theory, specifically the concept of a Cartesian product.

If you have a type A with a set of possible values and a type B with a set of possible values , the product type A * B has a set of possible values that is the Cartesian product . The total number of possible values for the product type is , hence the name "product".

A product type represents an "and" relationship: a value of the product type must contain a value of type A and a value of type B.

This next video makes this connection explicit.

Algebraic Data Types | OCaml Programming | Chapter 3 Video 17

In this follow-up video, the same instructor explains the 'algebra' of data types, formalizing why records and tuples are called product types.

Please watch these clips: Modeling Students with Records (00:46 - 01:41): Pay attention to the reasoning for choosing a record: a student has a name and an ID. Distinction between 'and' and 'or' (02:09 - 02:53): This is the key conceptual link. Note how 'and' leads to records/tuples. Product Types Explained (02:39 - 03:18): This segment directly defines records and tuples as 'product types' and connects them to the Cartesian product.

To summarize the logic:

  • A student has a name and an id.
  • A dessert has a sauce and a creamy_component and a crunchy_component.
  • This "and" conjunction implies that all components must be present simultaneously.
  • This corresponds to the mathematical notion of a Cartesian product, giving us the name product type.

3. Formalization in the ML Tradition

The ML language family (which includes Standard ML and OCaml) was pioneering in its development and use of a sophisticated static type system. The theoretical work behind it provides the formal basis for these concepts. As you're interested in Mitchell's work, let's look directly at a key paper he co-authored that formalizes the type structure of Standard ML.

This is a dense, academic paper. We won't parse the calculus in detail. Instead, our goal is to extract the high-level ideas that show how practitioners' tools (like struct) are grounded in formal theory.

On the type structure of standard ML

This paper, 'On the type structure of standard ML' by Robert Harper and John C. Mitchell, is a foundational text. We will look at how it formally models ML's structures, which are essentially records.

Please read the following excerpts with a focus on the conceptual strategy, not the symbolic details: In Section 7.1, 'Syntax', find the paragraph beginning 'Intuitively, general sums and products...'. Read this paragraph to see how the theory uses 'general cartesian products' and 'general sum type... consists of pairs' from Martin-Löf's type theory as its formal tools. In Section 7.3, 'Representing Modules in XML', read the first paragraph. Focus on the sentence: 'For example, the structure struct type t = Int val x: t = 3 end may be viewed as the pair (t: U1 = int, 3: t).' This is the crucial link between the language feature and its formal representation.

Let's distill the key insights from that paper regarding product types:

  1. Formal Tools: The theory doesn't start with records and tuples. It uses more fundamental logical constructs from Martin-Löf's type theory, namely general products and general sums. These are highly abstract versions of Cartesian products and disjoint unions.

  2. Modeling Structures as Pairs: The most important takeaway is how a concrete language feature is modeled. As you read, an ML structure (which is the language's powerful record system) is formally represented as a pair (a two-element tuple). The fields of the structure are then accessed via projection functions (fst and snd) on that pair.

This demonstrates a common pattern in programming language theory: a convenient, high-level feature like a record with named fields (student.name) is formally defined in terms of a simpler, more primitive construct like a tuple with positional access (snd(student_tuple)). The formal semantics only need to be defined for the primitive, and the high-level feature is then understood as "syntactic sugar" on top of it.

The paper's use of "general cartesian products" reinforces the connection we established earlier: the formalization of these compound data types is deeply rooted in the mathematics of products.

Conclusion

In this lesson, we have defined product types and traced their lineage from practical implementation to formal theory.

Key Takeaways:

  • Product Types are composite types that aggregate data by combining values from other types in an "and" relationship.
  • They are concretely implemented as tuples (components accessed by position) and records (components accessed by name).
  • The name "product" originates from the Cartesian product in set theory, as the set of possible values of a product type is the Cartesian product of its component types' value sets.
  • The ML language family was instrumental in popularizing and formalizing these types. Theoretical work, such as that by Mitchell and Harper, models complex language features like ML's structures (records) using more primitive formal constructs like pairs/tuples.

Next Lesson Preview:

We've just explored one half of the "algebra" in Algebraic Data Types—products. In the next lesson, we will investigate the other half: sum types. Where product types represent "and," sum types represent "or," allowing us to model data that can be one of several different things. This will complete our foundational understanding of how functional languages model data.

Can't find a good explanation? Sign up and we'll make it for you

Sign up