Structures
Alternative: choose one among many———86
Common encodings for alternatives———87
Combinations: a group of different values———88
Common encodings of combinations———89
Collections: multiple values———90
Common encodings for collections———90
Mappings: convert one value to another———91
Common encodings for mappings———91
Count: numerical quantities———92
Common encodings for quantities———92
Identifiers and names: unique reference———93
Supplement
Data Lens
85
In this supplement, we detail several common relationship structures that appear in the world along with language features to encode them.
You can use this supplement in two ways. Treat this as a reference for those structures and language features. And use it as inspiration for understanding new structures and new language features you encounter.
Supplement
Alternative: choose one among many
type Payment = CreditCard |
GiftCard |
App |
Cash |
LoyaltyCard |
Gratis ;
Counting the states
Alternatives are modeled as a sum type, what mathematicians call the structure of a “choice of one among many.” It is called a sum type because you calculate the number of its states by adding the states of all of the alternatives.
For example, if we encode the payment as on the right, we add up the states for CreditCard, GiftCard, App, etc.:
one of
Soymilk
Espresso
Almond
Chocolate
Hazelnut
one of
Raw
Burnt
Charcoal
Roast
one of
Super
Mega
Galactic
Size
Add In
one of
Gratis
Loyalty card
Cash
App
Gift card
Credit card
Payment
Alternatives are a common pattern found in domains. They denote a choice among several alternatives. Here are some examples from the coffee shop:
86
statesOf(Payment) = statesOf(CreditCard) + statesOf(GiftCard) +
statesOf(App) + statesOf(Cash) +
statesOf(LoyaltyCard) + statesOf(gratis)
Common encodings for alternatives
87
Data Lens
type DigitalPayment = CreditCard | GiftCard | App;
type AnalogPayment = Cash | LoyaltyCard | Comped;
type Payment = DigitalPayment | AnalogPayment;
interface Size {
name: string;
}
type Size = "super" |
"mega" |
"galactic";
Union type
enum Size {
super = "super",
mega = "mega",
galactic = "galactic",
}
Enum
Interface and classes
1
2
3
Natural numbers
Super
Mega
Galactic
Alternatives are encoded easily in most programming languages. Here are some common language features that share the “choice of one among many” structure. In this case, we are encoding the size:
class Super implements Size {
name = "super";
}
class Mega implements Size {
name = "mega";
}
class Galactic implements Size {
name = "galactic";
}
We can encode alternatives as natural numbers because natural numbers themselves are an alternative.
Numbers also have a natural ordering, like size. The biggest mis-fit is that the number of sizes is finite while natural numbers are infinite.
type NatNumber = 1 | 2 | 3 | ...;
Remember, we can use these language features to encode alternatives because they have the same “choose one among many” structure as alternatives.
Adding structure
Sum types give us lots of options for how to encode an alternative without losing fit. Besides using a different language feature, we could also introduce a new layer of structure. For instance, we could encode payment like this and get the same number of states:
Supplement
Combinations: a group of different values
Counting the states
Combinations are modeled as product type, what mathematicians call the structure of “a combination of multiple values.” It is called a product type because you calculate the number of its states by multiplying the states of its components.
For example, if we encode the coffee as on the right, we multiply the states of size, roast, and add ins:
Payment method
Barista
Delivery method
Order method
Coffees
Customer
all of
88
all of
Size
Roast
Add Ins
Combinations are a group of values of different kinds that are “combined” together into a larger whole. We might say that the structure of cominations are “all of” because they require all values to make the whole.
Coffee
Coffee order
type Coffee = {
size : Size;
roast : Roast;
addIns: AddIn[];
};
states(Coffee) = states(Size) × states(Roast) × states(AddIn[])
Common encodings of combinations
Data Lens
89
TypeScript Object
TypeScript Tuple
C Struct
Clojure map
TypeScript Class
Haskell data type
type Coffee = {
roast : Roast;
size : Size;
addIns: AddIn[];
};
type Coffee = [
Roast,
Size,
AddIn[]
];
struct Coffee {
Roast roast,
Size size,
AddIns addIns
};
{:roast :burnt
:size :super
:addIns [:espresso]}
class Coffee {
roast : Roast;
size : Size;
addIns: AddIn[];
};
data Coffee = Coffee {
roast :: Roast,
size :: Size,
addIns :: [AddIn]
}
Combinations are encoded easily in most programming languages. It is very common to have a way to combine multiple values together. Here are some common language features that share the structure “a combination of multiple values.”
Remember, these features are good for encoding combinations because they have the same structure as combinations. Matching the structure is vital for having good fit. But it is not the only concern. Another important concern is ergonomics. We’ll see other concerns in other lenses.
Supplement
Collections: multiple values
Common encodings for collections
Collections share the “some of” structure. But that’s not the whole story. Collections also have other structure, including:
Set
Map/JS Object
Array
List
some of
Collections are a group of values of the same kind. We’ll call the structure of collections “some of.”
some of
some of
Add In
Coffee
Customer
Add Ins
Coffees
Customers
90
91
Data Lens
Mappings: convert one value to another
Common encodings for mappings
Mappings relate one kind of value to another. You have one type and you can get another. Notice that it’s a one-way relationship.
JS Object
const sizePrice = {
super : 6,
mega : 7,
galactic: 8
};
sizePrice['super'] //=> 6
Map
const sizePrice = new Map([
['super' , 6],
['mega' , 7],
['galactic', 8]
]);
sizePrice.get('super') //=> 6
Function
function sizePrice(size) {
if(size === "galactic") {
return 8;
} else if(size === "mega") {
return 7;
} else if(size === "super") {
return 6;
} else {
throw "Unknown size";
}
}
sizePrice('super') //=> 6
Add In
Size
Price
Price
Coupon code
Discount
MAY2024
Supplement
Count: numerical quantities
Counts are numerical quantities. We use them all the time and they deserve a little attention.
There are a number of questions we need to ask when we encode counts:
$ Amount
% Discount
Volume
Common encodings for quantities
Integers
Floating point
Fixed Point
Combination with units
type MoneyAmount = {
amount : number;
currency: Currency;
};
92
Identifiers and names: unique reference
Common encodings for names
Common encodings for identifiers
Integers
UUID
Strings
Symbols/Keywords
Namespacing
We can avoid name collisions by prefixing a name with a namespace, another name that subdivides the space of names.
"size/medium"
"roast/medium"
We often need to refer to a thing in the real world and distinguish it from other similar things. For instance, we give users a username to distinguish the user from all others. Likewise, we give a bank account a number so we can refer to it. To do that, we use identifiers and names. The main difference between them is that names are human-readable.
Identifiers
Names
Username
Bank
account #
Order #
Student ID
License Plate
Size
Roast
Add In
Coupon code
MAY2024
Strings
93
Data Lens