build-a-spreadsheet-engine / lesson-05.md
Lesson 05 · Cells and A1 addressing

The cell value

A cell can hold different kinds of data - a number, some text, a boolean, or nothing at all - and later, an error. Today you build the one tagged value type that represents any cell's result, with every kind it will ever need, and teach it to render itself as a string.

The goal

Define a value type carrying a kind tag plus payload, and give it a String() that renders each kind for display.

Start here - the target
TO DO
Scenario: A value knows its kind and renders itself
Giventhe value kinds Empty, Number, Text, Bool, and Error
Whenvalues are constructed and rendered with String()
Thena Number value built from 42 reports kind Number, holds 42, and its String() is "42"; an Empty value reports kind Empty and String() is the empty string
Anda Bool true has String() "TRUE" and a Bool false has String() "FALSE", while a Text "hi" reports kind Text and String() "hi"
Background

Every cell eventually produces a value, and a spreadsheet’s values are not all the same type: A1 might be the number 42, B1 the text "total", C1 the boolean TRUE, and D1 empty. Rather than juggle separate types, we use one tagged union: a struct with a Kind tag and a field for each possible payload. The tag says which field is meaningful. Define the whole set of kinds now, even the ones we cannot produce yet - Err is here so that when error values like #DIV/0! and #CIRC! arrive in a later chapter, the value type already has a home for them.

Give the value a String() that renders each kind the way it should appear in a cell: an Empty shows as nothing, a Number as its digits (42, not 42.0), a Bool as TRUE or FALSE, a Text as itself, and an Err (later) as its code. This display method is the other half of the value type - the sheet and, eventually, a demo that prints the grid all lean on it. Front-loading the full kind set plus a renderer means no later lesson has to reshape this type; every evaluator, function, and error path just fills in the field its kind uses and gets a printable result for free.

Make it work
type Kind int
const (
Empty Kind = iota
Number
Text
Bool
Err // used later for #DIV/0!, #CIRC!, ...
)
type Value struct {
Kind Kind
Num float64
Str string
B bool
Code string // the error code, when Kind == Err
}
// String(): "" for Empty, the number for Number (no trailing zeros),
// the text for Text, "TRUE"/"FALSE" for Bool, the Code for Err.
CheckpointDONE
You have a single value type covering every kind a cell can hold, and it renders itself. Commit and stop here.