# Algebraic Data Types and Records — Haskell

Source: https://www.geekswithgeeks.com/en/haskell/t-adts

> Model data with sum types, product types, records and newtypes.

## Designing with types

**Algebraic data types** are declared with `data`. A **product type** combines values: `data Item = Item String Int` holds a SKU *and* a quantity. A **sum type** offers alternatives: `data Payment = Card String | Upi String | Cash` is *one of* these constructors. Most real types mix both, and constructors can be **parameterised** by types: `data Tree a = Leaf | Node (Tree a) a (Tree a)`. **Record syntax** names fields and generates accessor functions: `data Customer = Customer { name :: String, tier :: Tier }`, with **record update** `c { tier = Gold }` producing a modified copy. **`newtype`** wraps exactly one value in a distinct type at zero runtime cost, such as `newtype Paise = Paise Int`, preventing confusion between paise and rupees or customer and order IDs. **`type`** creates only an alias. Use `deriving (Show, Eq, Ord)` to generate common instances. Precise types make **illegal states unrepresentable**: rather than a status string plus an optional tracking number, use `Shipped TrackingId`, which can only exist with a tracking number.

## Sum and product types

A product holds several values together; a sum is one of several alternatives.

![Left: a single box divided into labelled compartments. Right: a fork in a path leading to three differently shaped boxes.](assets/figures/haskell/section-3-map.svg) — Figure 3.1 — Product types and sum types.

## Modelling orders with ADTs

Sum types, records, newtypes and exhaustive functions.

```haskell
newtype Paise = Paise Int deriving (Show, Eq, Ord)
newtype TrackingId = TrackingId String deriving (Show, Eq)

data Payment
  = Card { last4 :: String }
  | Upi String
  | Cash
  deriving (Show, Eq)

data OrderState
  = Placed
  | Paid Payment
  | Shipped Payment TrackingId
  | Cancelled String
  deriving (Show, Eq)

data Order = Order
  { orderId :: String
  , total   :: Paise
  , state   :: OrderState
  } deriving (Show, Eq)

describePayment :: Payment -> String
describePayment (Card digits) = "card ending " ++ digits
describePayment (Upi vpa)     = "UPI " ++ vpa
describePayment Cash          = "cash on delivery"

ship :: TrackingId -> Order -> Either String Order
ship tid order = case state order of
  Paid p -> Right order { state = Shipped p tid }          -- record update
  other  -> Left ("cannot ship an order that is " ++ show other)

main :: IO ()
main = do
  let o = Order { orderId = "o-1", total = Paise 49900, state = Paid (Upi "asha@okbank") }
  print (ship (TrackingId "TRK123") o)
  print (ship (TrackingId "TRK124") o { state = Placed })
  putStrLn (describePayment (Card "4242"))
```

## Avoid partial record fields

In `Payment`, `last4` exists only for `Card`, so `last4 Cash` crashes at run time. Prefer record syntax only for single-constructor types, or match on constructors; GHC can warn with `-Wpartial-fields`.

**Quiz:** What does newtype Paise = Paise Int give you?

- [x] A distinct type wrapping Int with no runtime cost
- [ ] A type alias with no checking
- [ ] A mutable integer
- [ ] A list of integers

*Answer:* A distinct type wrapping Int with no runtime cost. newtype creates a new type for the compiler that is erased at run time.
