A recipe is more than a collection of strings and numbers: ingredients, measurements, steps, and yields have different roles. A type system supplies rules for defining and using those values. In Go, named types and structs make domain concepts explicit, while strict compile-time checking catches many mismatches before a program runs. section 5.0 section 5.1
Start with named types where a primitive alone does not express intent. Compose structs to group related values, then use slices and maps for collections such as ingredient lists and grocery lists. This approach builds richer models from simpler types rather than relying on inheritance. section 5.1 section 5.2.2
type Ingredient string
type IngredientMeasurement struct {
Ingredient Ingredient
Measurement Convertible #1
}
type IngredientList []IngredientMeasurement #2
type Step struct {
Description string
Ingredients IngredientList
}
The type definitions show the progression from an ingredient name to a measured ingredient and a step containing a list of them. The Convertible field gives measurements a shared behavioral contract. section 5.2.2
Methods add behavior to types; interfaces specify behavior that different types can share. In the recipe model, measurement types support conversion between unit systems, and recipe methods support scaling. Pointers matter when methods modify receiver state. When an interface value’s concrete type matters at runtime, type assertions and type switches provide checks. section 5.6.2 section 5.7
From recipe to grocery list
A pancake recipe contains steps with measured ingredients. After scaling the recipe, CreateGroceryList walks its steps, converts measurements to metric values, and combines amounts by ingredient. Named collection types keep the resulting grocery list distinct in purpose from the recipe’s steps. section 5.6.2 section 5.2.2





