Values and bindings

Chapter 1 left us with two names, price and items, and an error when we tried to multiply them. We’ll come back to that. First, give a few values names of their own:

let name = "Ada"
let messages = 3
print("\(name) has \(messages) messages.")
Ada has 3 messages.

let binds a name to a value. Here name holds text and messages holds a whole number. Hirundo works out their types from what you gave them; you don’t need to write String or Int beside each name. The interpolation from hello.hir works with numbers too.

A let binding keeps the same value. When the value needs to change, use var instead:

var messages = 3
messages = messages + 1
print("Now there are \(messages) messages.")
Now there are 4 messages.

The assignment changes messages from 3 to 4. Its type stays Int; reassignment doesn’t let the name switch to a String halfway through the program. An annotation works on var too, though this one needs none.

A type worth writing down

An annotation goes after the name. Most of the time inference is enough, but an annotation can tell Hirundo which type you want when a literal would otherwise leave it to choose:

let ratio: Double = 2
print(ratio * 1.25)
print(2.5 * 3)
2.5
7.5

let ratio: Double = 2 asks for a Double. The 2 can supply one even without a decimal point. The next line multiplies that Double by 1.25. The last line works for the same reason: in 2.5 * 3, the integer literal 3 can become a Double while the expression is being typed.

On its own, a whole-number literal normally becomes an Int; a decimal literal normally becomes a Double. Once either value is bound to a name, that name has a type. It doesn’t keep the literal’s flexibility.

Back to the error

Here’s the program from chapter 1:

let price = 2.5
let items = 2
print(price * items)
error: 3:13 cannot multiply a Double by an Int

items is already an Int. Hirundo won’t silently turn it into a Double to match price, though the literal 2 could have taken on that type directly in price * 2. To make the choice yourself, write Double(items). Try it with three items:

let price = 2.5
let items = 3
print(price * Double(items))
7.5

This time the multiplication has two Double operands. The conversion is visible where it matters; items itself hasn’t changed type.

Questions with true-or-false answers

A comparison produces a Bool: true or false. The > operator asks whether the left number is greater; == asks whether two numbers are equal. The two equals signs matter: a single = is the assignment you used with messages. Try both comparisons:

let temperature = 22.5
let threshold = 23
print("Past 22? \(temperature > 22)")
print("At 22.5? \(temperature == 22.5)")
print("Past threshold? \(temperature > Double(threshold))")
Past 22? true
At 22.5? true
Past threshold? false

The 22 in the first comparison can take its type from temperature. The bound threshold cannot, so the last comparison uses Double(threshold). The rule is the same one you met with multiplication; a comparison doesn’t get a secret conversion of its own. Other numeric comparisons use !=, <, <=, and >=, and also answer with a Bool.

You have met four useful types: Int for whole numbers, Double for decimals, String for text, and Bool for true or false. These four may carry you further than you expect.

Next, we’ll use a Bool to let a program choose what to do.