15-150: Principles of Functional Programming

Lecture 14: Structures and Signatures

In this lecture, we introduced the SML module system, a sophisticated set of features designed to allow the writing of composable, organized software in SML.

We discussed the value of namespaces in organizing separate collections of functionality. We also desire to not pollute our namespaces with helpers that do not need to be used by external consumers. To that end, we found that SML not only allows us to package our code into structures, but it offers us the ability to describe our structures by signatures, which are essentially "types for modules".

One of the great wins we found with signatures in its ability to let us abstract away implementation details of modules. Using signatures which only describe the basic shape of the API that we are implementing, and not saying anything about how the functionality is achieved, we allow ourselves to program in a way where we can be agnostic to implementation details, relieving us of cognitive burden.

We found that SML lets us "dial this up to eleven" using a distinction between transparent and opaque ascription to signatures. Transparent ascription lets us see the definition of types included in the module, whereas opaque ascription gives us the option to hide the definition of a type. This means that consumers of an API are not allowed to interact with the "raw" data contained within a particular value, but only through the functions given through the API. When dealing with values that have invariants that must be maintained, this is tantamount to ensure that consumers do not accidentally break a contract.

Abstract types let us establish a property of representation independence, essentially showing that we can write code that is provably the same regardless of the particular representation chosen by the library, so long as certain laws are obeyed, dependent on the library. In particular, we can view the values of two different representations of an abstract type as being grouped into "representationally equivalent" classes, such that there is no way to write code that can distinguish between representatives of equivalent classes.

Slides