---
title: "Swift's Design Goals"
source: 'https://academia.sh/en/courses/swift/design-goals-of-swift'
course: 'The Swift Language'
language: en
updated: '2026-10-21T05:03:11+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Relate Swift's safety, expressiveness and performance goals to concrete behavior"
    - 'Classify the moment an expression is evaluated as immediately, on first use, on every use or never'
    - 'Explain the rule and the limit of the `Probe` class in the measurement harness'
---

# Swift's Design Goals

Ties Swift's three goals — safety, expressiveness, performance — to one measurement axis: when is this expression evaluated? Splits eight expressions into four moments.

In earlier courses you asked what a program does. This course asks something else: when you write an expression, at which moment of the program does it run? You saw the split between compile time and run time in an earlier course (M11/K01); here we measure the order inside the run. Swift is the language of the iOS curriculum, and it is customary to introduce it through three goals.

## Three Goals

Safety: the language tries to catch errors before the run. A constant cannot be read before it is assigned, and a value cannot be `nil` unless its type is an optional. In both cases the compiler rejects the program. These snippets do not compile in Swift 6.4; the compiler rejects both, which is why they have no output.

```swift
let total: Int
print(total)
```

```swift
var name: String = nil
```

Expressiveness: you do not have to write the type, because the compiler infers it from the right-hand side (type inference). The performance goal is not measured in this lesson; we do not measure time, only order.

## The Measurement Axis: Four Moments

Throughout the course we ask a single question: when is this expression evaluated? There are four answers: **immediately**, **on first use**, **on every use** and **never**. The measurement harness has two parts. First comes the helper file; it stays the same through the whole lesson and always stands here as the first Swift block.

```swift
// Probe.swift — records when an expression is evaluated
final class Probe {
    let label: String
    private(set) var evaluations = 0
    private(set) var uses = 0
    private var evaluatedBeforeUse = false

    init(_ label: String) {
        self.label = label
    }

    func eval<T>(_ value: T) -> T {
        evaluations += 1
        if uses == 0 { evaluatedBeforeUse = true }
        return value
    }

    func use<T>(_ access: () -> T) -> T {
        uses += 1
        return access()
    }

    var moment: String {
        if evaluations == 0 { return "never" }
        if evaluations > 1 { return "on-every-use" }
        return evaluatedBeforeUse ? "immediately" : "on-first-use"
    }

    func report() {
        print("\(label): \(evaluations) evaluations, \(uses) uses -> \(moment)")
    }
}

func tally(_ probes: [Probe]) {
    for moment in ["immediately", "on-first-use", "on-every-use", "never"] {
        let count = probes.filter { $0.moment == moment }.count
        print("\(moment): \(count)")
    }
}
```

`eval` wraps the expression and counts that it was evaluated; `use` wraps the access to the expression. The rule is simple: if it was never evaluated, `never`; if it was evaluated more than once, `on-every-use`; if it was evaluated once and that happened before the first use, `immediately`; otherwise `on-first-use`.

**LB1.** Every observation except `never` carries at least two uses; with a single use, one evaluation cannot tell `immediately` and `on-first-use` apart.

Eight expressions, eight `Probe` objects. A constant, a lazy property, a computed property, a function, a short circuit and a closure sit side by side in the same file; the course will split them into separate lessons one by one.

```swift
// main.swift — eight expressions, four moments
final class Basket {
    let lazyProbe: Probe
    let computedProbe: Probe
    var items = [3, 4, 5]

    init(lazyProbe: Probe, computedProbe: Probe) {
        self.lazyProbe = lazyProbe
        self.computedProbe = computedProbe
    }

    lazy var total: Int = lazyProbe.eval(items.reduce(0, +))
    var count: Int { computedProbe.eval(items.count) }
}

func square(_ x: Int, probe: Probe) -> Int { probe.eval(x * x) }

let constant = Probe("let-constant")
let lazyTotal = Probe("lazy-property")
let computed = Probe("computed-property")
let fallback = Probe("nil-coalescing-rhs")
let call = Probe("function-body")
let shortCircuit = Probe("and-operator-rhs")
let captured = Probe("capture-list")
let body = Probe("closure-body")

let limit = constant.eval(10)
_ = constant.use { limit }
_ = constant.use { limit }

let basket = Basket(lazyProbe: lazyTotal, computedProbe: computed)
_ = lazyTotal.use { basket.total }
_ = lazyTotal.use { basket.total }
_ = computed.use { basket.count }
_ = computed.use { basket.count }

let stored: Int? = 7
_ = fallback.use { stored ?? fallback.eval(0) }
_ = fallback.use { stored ?? fallback.eval(0) }

_ = call.use { square(3, probe: call) }
_ = call.use { square(4, probe: call) }

let ready = false
_ = shortCircuit.use { ready && shortCircuit.eval(true) }
_ = shortCircuit.use { ready && shortCircuit.eval(true) }

let step = 2
let addStep = { [step = captured.eval(step)] (x: Int) -> Int in x + step }
_ = captured.use { addStep(1) }
_ = captured.use { addStep(2) }

let double = { (x: Int) -> Int in body.eval(x * 2) }
_ = body.use { double(3) }
_ = body.use { double(4) }

let probes = [constant, lazyTotal, computed, fallback, call, shortCircuit, captured, body]
for probe in probes { probe.report() }
tally(probes)
```

```
let-constant: 1 evaluations, 2 uses -> immediately
lazy-property: 1 evaluations, 2 uses -> on-first-use
computed-property: 2 evaluations, 2 uses -> on-every-use
nil-coalescing-rhs: 0 evaluations, 2 uses -> never
function-body: 2 evaluations, 2 uses -> on-every-use
and-operator-rhs: 0 evaluations, 2 uses -> never
capture-list: 1 evaluations, 2 uses -> immediately
closure-body: 2 evaluations, 2 uses -> on-every-use
immediately: 2
on-first-use: 1
on-every-use: 3
never: 2
```

The eight expressions split into four moments: two `immediately`, one `on-first-use`, three `on-every-use`, two `never`. This count is the course's binding tally; the later lessons open each expression in its own lesson. A `let` constant is evaluated on the line where it is defined. A `lazy` property is built when it is first read and then stored, while a computed property is calculated again on every read. A function body runs on every call; the right side of the `??` operator never runs because the left side is filled, and the right side of the `&&` operator never runs because the left side is false. A capture list is evaluated once, when the closure is created; the closure's body is evaluated on every call.

**LB2.** The constant and the capture list land on the same moment, but by different paths; this lesson measures only the moment, not the mechanism.

## A Value Copy Does Not Recompute

One leg of Swift's safety goal is value semantics: value types such as arrays, strings and structures are copied on assignment, and the copy is an independent value. When you assign one variable to another, changing either one does not affect the other. This property comes from the design concern that no state should be shared by accident. The measurement question is a different one: does the copy re-evaluate the expression that produced the source, or does it merely duplicate the existing value?

The block below produces an array with a `Probe`, copies it into a second variable named `duplicate`, and changes the two arrays independently. It then reads both variables; the reads count as two uses.

```swift
// Copy.swift — a copy does not run the expression again
let sourceProbe = Probe("source-array")
var original = sourceProbe.eval([1, 2, 3])
var duplicate = original
duplicate.append(4)
original.append(9)
_ = sourceProbe.use { original.count }
_ = sourceProbe.use { duplicate.count }
print(original, duplicate)
sourceProbe.report()
```

```
[1, 2, 3, 9] [1, 2, 3, 4]
source-array: 1 evaluations, 2 uses -> immediately
```

The 9 appended to `original` did not show up in `duplicate`, and the 4 appended to `duplicate` is not in `original`. The two variables separated at the moment of assignment and each went its own way. The measurement line says the expression was evaluated only once: assignment duplicates a value, it does not run the expression that produced it again. Because the evaluation happens in the `var` definition, before the two reads, the moment comes out as `immediately`.

This distinction makes the safety goal concrete. If assignment recomputed the source, every copy could produce its result at a different moment; in the measured behavior, a copy is a snapshot of the value at the moment of assignment. Later changes apply to each side separately.

**LB3.** The independence of the copy was measured with an array of `Int`; sharing of elements in the copy of an array that holds class instances is not the subject of this lesson.

## A Constant Binding Does Not Freeze the Object

Another end of the safety goal concerns `let`, and it is often misread. When you bind a class instance with `let`, what is bound is the reference to the instance. Writing a constant instead of a variable stops the name `holder` from pointing at another object, but it does not lock the object's own `var` properties. You can read the same rule the other way round for value types: if you bind a structure with `let`, you cannot change its properties either; a class has no such ban.

The block below defines a small class, binds it with `let`, and still changes the counter inside it with two assignments. The counter value comes through a `Probe`; two separate assignments bring two separate evaluations.

```swift
// Holder.swift — let does not freeze the object
final class Holder {
    var count = 0
}
let holderProbe = Probe("class-counter")
let anchor = Holder()
_ = holderProbe.use { anchor.count = holderProbe.eval(5) }
_ = holderProbe.use { anchor.count = holderProbe.eval(8) }
print(anchor.count)
holderProbe.report()
```

```
8
class-counter: 2 evaluations, 2 uses -> on-every-use
```

Although `anchor` is a `let` constant, `anchor.count` was assigned twice and the final value, 8, was read. What is constant is the reference that the name `anchor` is bound to; the inside of the object is variable. The measurement line shows two evaluations, because the two assignments made two separate `eval` calls: the `let` rule does not restrict the number of assignments here, since what is assigned is not `anchor` but the `anchor.count` field. That is why the moment is `on-every-use`.

The design takeaway is this: `let` guarantees that a binding cannot be made again; it does not guarantee that the object is immutable. If you want immutability, make the fields `let` as well, or use a value type.

**LB4.** The class instance was measured with a single field; the claim that the same assignment does not compile on a value type bound with `let` lies outside this lesson, as source-level knowledge.

## Type Inference Does Not Change the Moment

The most visible part of the expressiveness goal is type inference: if you write `let size = 12`, the compiler finds the `Int` type itself and you do not have to write it. If you like, you can also write the type explicitly: `let size: Int = 12`. The question that comes to mind is whether inference has a run-time cost or a timing. Type inference finishes at compile time; the running program does not know how the type was found.

The block below defines the same value in two ways: one by inference, one with an explicit type annotation. Each is tied to its own `Probe` and read twice; the types of both variables are printed with `type(of:)`.

```swift
// Inference.swift — inference and an explicit type give the same moment
let inferredProbe = Probe("inferred-type")
let declaredProbe = Probe("explicit-type")
let inferred = inferredProbe.eval(42)
let declared: Int = declaredProbe.eval(42)
_ = inferredProbe.use { inferred + 1 }
_ = inferredProbe.use { inferred + 2 }
_ = declaredProbe.use { declared + 1 }
_ = declaredProbe.use { declared + 2 }
print(type(of: inferred), type(of: declared))
inferredProbe.report()
declaredProbe.report()
```

```
Int Int
inferred-type: 1 evaluations, 2 uses -> immediately
explicit-type: 1 evaluations, 2 uses -> immediately
```

The two variables print the same type, and the two `Probe` objects gave the same moment: both are one evaluation, two uses, `immediately`. The explicit type annotation did not move the expression to another time; it only told the compiler what to find. This is why the course's measurement axis is insensitive to type inference: what determines when an expression is evaluated is not whether the type is written, but the context in which the expression stands.

**LB5.** Type inference was measured in this lesson only with an `Int` literal; the claim that inference leaves timing untouched for other literals and kinds of expression lies outside this lesson.

## A Lazy Transformation Waits Until It Is Called

The expressiveness goal of the design also raises the question of when something written briefly actually runs. When you call `map` on an array, the transformation is done immediately, for every element. If you call `map` on the `lazy` view of the same array, the transformation runs for no element at the moment it is defined; it runs only when an element is requested, and only for that element. This is not the array counterpart of the `lazy var` property we saw earlier, but it carries the same intuition: work can be deferred until someone asks for the result.

The block below sets up two `Probe` objects. The body of the eager transformation records evaluations on `eagerProbe`, and the body of the lazy transformation records them on `lazyProbe`. The counters are printed right after the transformations and again after two elements are pulled from the lazy view. The `evaluations` property is a readable counter inside `Probe`.

```swift
// LazyMap.swift — eager and lazy transformation
let eagerProbe = Probe("eager-transform")
let lazyMapProbe = Probe("lazy-transform")
let values = [1, 2, 3]
let eagerDoubled = values.map { eagerProbe.eval($0 * 2) }
let lazyDoubled = values.lazy.map { lazyMapProbe.eval($0 * 2) }
print(eagerProbe.evaluations, lazyMapProbe.evaluations)
_ = lazyMapProbe.use { Array(lazyDoubled.prefix(2)) }
print(eagerProbe.evaluations, lazyMapProbe.evaluations)
_ = lazyMapProbe.use { Array(lazyDoubled.prefix(2)) }
print(eagerDoubled, Array(lazyDoubled.prefix(1)))
lazyMapProbe.report()
```

```
3 0
3 2
[2, 4, 6] [2]
lazy-transform: 5 evaluations, 2 uses -> on-every-use
```

The first line came out as `3 0`: the eager `map` transformed all three elements immediately, while the lazy `map` had not yet touched any. On the second line the lazy counter became 2, because `prefix(2)` asked for the transformation of only two elements; the third element was never computed. When the same `prefix(2)` was called a second time, the transformation ran again, because a lazy view does not store its results; the five evaluations on the last `report` line show this: two, two, and one from the final `prefix(1)` call.

The expressiveness goal leaves it to you to tell these two behaviors apart. The eager `map` produces the result once and stores it. The lazy view keeps the writing short, but it may redo the work on every pull. Which is right depends on how many times the result will be read and whether the body has a side effect.

**LB6.** The lazy transformation was measured only for elements pulled with `prefix`; how the `lazy` view changes the count under `filter` or chained transformations lies outside this lesson.

## The Bounding Measurement

The rule counts, it does not read intent. If you write the same `let` expression as two separate constants with two separate `Probe` objects, both come out `immediately`. If you tie the same expression to a single `Probe`, the second `eval` raises the count to two and the rule says `on-every-use`; the intent of the code has not changed, but the measurement harness has.

```swift
// Twin.swift — the same expression, two probes and one probe
let first = Probe("first-constant")
let second = Probe("second-constant")
let shared = Probe("shared-constant")

let a = first.eval(5)
let b = second.eval(5)
let c = shared.eval(5)
let d = shared.eval(5)

for probe in [first, second, shared] {
    _ = probe.use { a + b + c + d }
    _ = probe.use { a + b + c + d }
    probe.report()
}
```

```
first-constant: 1 evaluations, 2 uses -> immediately
second-constant: 1 evaluations, 2 uses -> immediately
shared-constant: 2 evaluations, 2 uses -> on-every-use
```

**LB7.** This measurement reads the number of `eval` calls, not how the expression is written; we do not count `Probe` as a language feature.

## Summary

- Swift provides safety through the compiler: an unassigned constant cannot be read, and a non-optional value cannot be `nil`.
- Expressiveness is type inference; the performance goal is not measured in this lesson.
- The course's measurement axis: when is this expression evaluated? Four moments: immediately, on first use, on every use, never.
- The `Probe` rule reads the count; it does not read intent.
- This lesson's moment: **immediately** (a `let` constant, on the line where it is defined).

## Next Step

A constant is evaluated the moment it is defined; but what happens when a `var` is reassigned, and can a constant be assigned after it has been defined? The next lesson measures the assignment moments of constants and variables.
