# 3. Common programming concepts

> Chapter 3: values and variables, val, types and inference, numbers, nil safety, methods, control flow, and where Alice differs from Ruby.

Canonical: https://alice-lang.org/documentation/learn/common-concepts/

[Documentation index](https://alice-lang.org/documentation/llms.txt) · [Complete book](https://alice-lang.org/llms-full.txt)

The guessing game used variables, numbers, `nil` checks, methods, `if` and loops without explaining them. This chapter explains them, one idea per section. These ideas exist in almost every language, so the interesting part is how Alice handles each one, and why.

## 3.1 Variables

A variable is a name for a value. You create one by assigning to it:

```alice
cups = 3
puts cups
cups = cups + 2
puts cups
```

```text title="Output"
3
5
```

There is no keyword such as `let` or `var`. The first assignment creates the variable, and later assignments change it.

### A variable keeps its type

Alice looks at the first value and decides the variable's type: `cups` is an `Int32`, a 32-bit integer. From then on, the variable may only hold `Int32` values:

```alice fail="AL0304"
cups = 3
cups = "three"
```

```text title="Error"
error[AL0304]: type mismatch: expected Int32, got String
```

Why be so strict? Because a variable that is a number on one line and a string on the next is a common source of bugs in dynamic languages. A fixed type also lets the compiler produce fast code: an `Int32` lives in a CPU register, not in a box on the heap.

### Values that never change: `val`

Some values should never change after you set them: a tax rate, the size of a board, a configuration loaded at startup. Mark them with `val`:

```alice fail="AL0203"
val tax_percent = 7
tax_percent = 8
```

```text title="Error"
error[AL0203]: cannot assign to immutable 'tax_percent' (declared with val)
```

`val` protects the name, not the object behind it. A `val` that holds an array cannot be pointed at a different array, but the elements of that array can still change. [Chapter 6](https://alice-lang.org/documentation/learn/types.md) shows how to make whole objects read-only.

A name in capital letters at the top level of a file is a **constant**. Methods can read it:

```alice
MAX_CUPS = 10

def can_order?(cups : Int32) : Bool
  cups <= MAX_CUPS
end

puts can_order?(3)
puts can_order?(12)
```

```text title="Output"
true
false
```

## 3.2 Types and inference

Every value has a type. These are the ones you will use most:

| Type      | Example                 | What it holds                                  |
| --------- | ----------------------- | ---------------------------------------------- |
| `Int32`   | `42`, `-7`, `1_000_000` | whole numbers, about plus or minus 2.1 billion |
| `Int64`   | `42_i64`                | whole numbers up to about 9.2 quintillion      |
| `Float64` | `3.14`, `1e-7`          | numbers with a fractional part                 |
| `Bool`    | `true`, `false`         | yes or no                                      |
| `Char`    | `'a'`                   | one character                                  |
| `String`  | `"tea"`                 | text                                           |

The underscore in `1_000_000` is only for your eyes. A suffix such as `_i64`, `_u8` or `_f32` picks a different number type.

You rarely write types for variables. Alice **infers** them from the values. Where you want to be explicit, write `name : Type = value`:

```alice
price : Int64 = 250
ratio : Float64 = 3
puts price
puts ratio
```

```text title="Output"
250
3.0
```

`ratio` holds `3.0`, not `3`: the annotation made Alice store the integer as a float. Floats always print with a decimal point, so you can tell them apart.

Method parameters are where types matter most. They are the contract between the caller and the method, so you usually write them. Return types can be inferred, but writing them documents the method.

### Converting between types

Mixing `Int32` and `Int64`, or integers and floats, in arithmetic works: Alice widens the smaller type. In the other direction you convert explicitly, because information may be lost:

```alice
cups = 7
puts cups.to_f / 2
puts 3.7.to_i
puts 3.7.round
puts cups.to_s + " cups"
puts "12".to_i + 1
```

```text title="Output"
3.5
3
4.0
7 cups
13
```

`to_i` on a float cuts off the fraction. `round` rounds but keeps the float type. `to_s` turns anything into text.

## 3.3 Numbers that behave

Two rules about integers surprise people who come from C# or Java. One surprises people who come from Ruby.

### Integer division rounds down

Dividing two integers gives an integer. Alice rounds the result **down**, toward negative infinity, as Ruby and Python do. C#, Java and C round toward zero instead. The difference only shows with negative numbers:

```alice
puts 7 / 2
puts -7 / 2
puts -7 % 3
puts (-7).tdiv(2)
puts 7.0 / 2
```

```text title="Output"
3
-4
2
-3
3.5
```

Rounding down keeps a useful promise: `a % b` has the sign of `b`, so `n % 7` is always between 0 and 6, even for negative `n`. That is what you want for days of the week, clock arithmetic and array wrap-around. If you need C#'s behavior, `tdiv` and `remainder` truncate toward zero.

### Overflow is an error, not a surprise

An `Int32` cannot hold more than 2,147,483,647. What should happen when a result is bigger? C# wraps around silently to a large negative number. Ruby switches to a big number type, which is slower.

Alice does neither: it stops the program with an `OverflowException`. A wrong total is worse than a crash, because it can travel far before anyone notices.

```alice
def total_cents(price : Int32, quantity : Int32) : Int32
  price * quantity
end

puts total_cents(250, 10_000_000)
```

```text title="Runtime error"
OverflowException: Arithmetic operation resulted in an overflow.
```

The fix is a bigger type where the numbers can be big: `price.to_i64 * quantity`. When wrapping is really what you want, as in hash functions and random number generators, say so with `&+`, `&-` and `&*`:

```alice
x = Int32::MAX
puts x &+ 1
```

```text title="Output"
-2147483648
```

## 3.4 Nil: the value that is not there

Sometimes a value is missing: a search found nothing, the input ended, the user left a field empty. Alice represents that with `nil`. But a variable may only hold `nil` if its type says so with a `?`:

- `String` is always a string.
- `String?` is a string or `nil`.

This sounds like a small detail. It removes the most common crash in programming, the null reference error, because the compiler knows exactly which values may be missing.

```alice
def find_tea(menu : Array(String), letter : String) : String?
  menu.find { |name| name.starts_with?(letter) }
end

menu = ["Assam", "Darjeeling", "Earl Grey"]
tea = find_tea(menu, "D")
if tea
  puts tea.upcase
end
puts find_tea(menu, "Z").nil?
```

```text title="Output"
DARJEELING
true
```

`find` returns `String?`, because it might find nothing. Inside `if tea`, the compiler knows that `tea` is a plain `String`, so `upcase` is allowed. Outside the check it is not:

```alice fail="AL0310"
def find_tea(menu : Array(String), letter : String) : String?
  menu.find { |name| name.starts_with?(letter) }
end

puts find_tea(["Assam"], "A").upcase
```

```text title="Error"
error[AL0310]: value may be nil here
```

There are four ways to deal with a `T?`. Pick the one that says what you mean:

| You write                          | Meaning                                        |
| ---------------------------------- | ---------------------------------------------- |
| `if x ... end`                     | do something only when there is a value        |
| `x \|\| fallback`                  | use a default when there is none               |
| `return unless x`, `next unless x` | leave early when there is none                 |
| `x.not_nil!`                       | "I know it is there": crashes if you are wrong |

```alice
discount : Int32? = nil
puts discount || 0
discount = 15
puts discount || 0
```

```text title="Output"
0
15
```

Notice that `discount = nil` alone would not compile. Alice cannot guess what type the missing value would have had, so you write it: `discount : Int32? = nil`.

## 3.5 Methods

A method gives a piece of code a name, so you can use it many times and test it alone. You have seen the shape already:

```alice
def line_total(price : Int32, quantity : Int32 = 1) : Int32
  price * quantity
end

puts line_total(250, 3)
puts line_total(400)
```

```text title="Output"
750
400
```

- `quantity : Int32 = 1` is a parameter with a default value. The second call leaves it out.
- The last expression is the result. Use `return` only to leave early.
- Parentheses around arguments are optional in a statement: `puts line_total 400` works too. Keep them when an expression gets long.

A one-line method can use `=`:

```alice
def cents_to_text(cents : Int32) : String = "#{cents / 100}.#{(cents % 100).to_s.rjust(2, "0")}"

puts cents_to_text(750)
puts cents_to_text(1205)
```

```text title="Output"
7.50
12.05
```

A method whose name ends in `?` answers a yes-or-no question and returns a `Bool`, like `can_order?` above. It is a naming habit from Ruby that makes conditions read well: `if can_order?(cups)`.

To return two values, return a tuple and unpack it:

```alice
def min_max(prices : Array(Int32)) : {Int32, Int32}
  {prices.min, prices.max}
end

low, high = min_max([250, 400, 175])
puts "from #{low} to #{high}"
```

```text title="Output"
from 175 to 400
```

## 3.6 Control flow

### Choosing with `if` and `case`

You know `if`, `elsif` and `else` from the game. Two more forms keep simple decisions short:

```alice
cups = 12
puts "That's a lot of tea." if cups > 10
puts "Small order." unless cups > 10
size = cups > 10 ? "large" : "small"
puts size
```

```text title="Output"
That's a lot of tea.
large
```

In Alice, `if` and `case` are **expressions**: they produce a value. That replaces the temporary variables you would otherwise assign in each branch:

```alice
cups = 7
label = case cups
        when 0 then "nothing"
        when 1..3 then "a few"
        when 4, 5, 6 then "several"
        else "plenty"
        end
puts label
```

```text title="Output"
plenty
```

`when` accepts single values, lists of values and ranges. The first matching branch wins.

### Repeating

```alice
for i in 1..3
  puts "cup #{i}"
end

3.times { |i| print i, " " }
puts

n = 0
while n < 100
  n += 30
end
puts n
```

```text title="Output"
cup 1
cup 2
cup 3
0 1 2 
120
```

`1..3` is a range from 1 to 3 inclusive; `1...3` excludes the end. `3.times { |i| ... }` runs a **block**, the code in braces, three times with `i` from 0 to 2. Blocks are everywhere in Alice, and [chapter 4](https://alice-lang.org/documentation/learn/collections.md) is full of them. `while` repeats while a condition holds; `until` repeats until it holds.

`loop do ... end` repeats until `break`, and `break` can return a value from the loop:

```alice
n = 1
first_big = loop do
  n *= 3
  break n if n > 100
end
puts first_big
```

```text title="Output"
243
```

## Project: prices for the tea shop

Let's use what you learned in the tea shop. Prices are constants, the total uses `Int64` so big orders cannot overflow, and a discount rounds down in the shop's favor.

```alice
TEA_CENTS = 250
SCONE_CENTS = 400

def order_total(teas : Int32, scones : Int32) : Int64
  teas.to_i64 * TEA_CENTS + scones.to_i64 * SCONE_CENTS
end

def discounted(total : Int64, percent : Int32) : Int64
  total - total * percent / 100
end

def money(cents : Int64) : String
  "#{cents / 100}.#{(cents % 100).to_s.rjust(2, "0")}"
end

total = order_total(3, 2)
puts "Total: #{money(total)}"
puts "With 15% off: #{money(discounted(total, 15))}"
puts "A big party: #{money(order_total(10_000_000, 0))}"
```

```text title="Output"
Total: 15.50
With 15% off: 13.18
A big party: 25000000.00
```

15 percent of 1550 is 232.5. Integer division rounds that down to 232, so the discounted price is 1318 cents.

:::caution\[Common mistakes]

- **`x = nil` with no type.** Alice cannot infer a type from `nil`: `error[AL0204]`. Write `x : String? = nil`.
- **Using a `T?` without a check.** `error[AL0310]`. Check it with `if x`, give a default with `x || y`, or leave early with a guard.
- **Expecting C#'s division.** `-7 / 2` is `-4` in Alice. Use `tdiv` for truncation.
- **Computing money in `Int32` and floats.** Products can overflow, and floats round. Use integer cents in `Int64`.
- **Reassigning a `val`.** `error[AL0203]`. Use a plain variable if the value must change.
  :::

## Exercises

1. Write `fahrenheit(celsius : Float64) : Float64` and print the result for `100.0`.
2. What do `-9 / 4`, `-9 % 4` and `(-9).tdiv(4)` print? Predict first, then run them.
3. Write `day_name(day : Int32) : String` that maps 0 to `"Sun"` through 6 to `"Sat"` with `case`, and returns `"?"` otherwise.
4. Write `first_long(words : Array(String)) : String?` that returns the first word longer than 5 letters. Print its result for `["tea", "biscuit"]`, and print `none` when there is no such word.

<details>
<summary>Answers</summary>

1. Answer:

   ```alice
   def fahrenheit(celsius : Float64) : Float64
     celsius * 9 / 5 + 32
   end

   puts fahrenheit(100.0)
   ```

   ```text title="Output"
   212.0
   ```

2. Division rounds down, and the remainder has the sign of the divisor:

   ```alice
   puts -9 / 4
   puts -9 % 4
   puts (-9).tdiv(4)
   ```

   ```text title="Output"
   -3
   3
   -2
   ```

3. Answer:

   ```alice
   def day_name(day : Int32) : String
     case day
     when 0 then "Sun"
     when 1 then "Mon"
     when 2 then "Tue"
     when 3 then "Wed"
     when 4 then "Thu"
     when 5 then "Fri"
     when 6 then "Sat"
     else "?"
     end
   end

   puts day_name(5)
   puts day_name(9)
   ```

   ```text title="Output"
   Fri
   ?
   ```

4. `find` already returns `String?`, and `||` supplies the text for the missing case:

   ```alice
   def first_long(words : Array(String)) : String?
     words.find { |w| w.size > 5 }
   end

   puts first_long(["tea", "biscuit"]) || "none"
   puts first_long(["tea"]) || "none"
   ```

   ```text title="Output"
   biscuit
   none
   ```

</details>

## Summary

- Assignment creates a variable. Its type is fixed by the first value. `val` forbids reassignment.
- Types are inferred. Write them on parameters, and where you want to document or convert.
- Integer `/` and `%` round down. Integer overflow is an `OverflowException`; `&+ &- &*` wrap on purpose.
- A `T?` may be `nil`. The compiler makes you check it before use, and narrows it to `T` after the check.
- `if` and `case` produce values. `for`, `while`, `times` and `loop` repeat.

Next, [chapter 4](https://alice-lang.org/documentation/learn/collections.md) stores many values at once.

## Continue reading

- Previous: [2. A guessing game](https://alice-lang.org/documentation/learn/guessing-game.md)
- Next: [4. Collections](https://alice-lang.org/documentation/learn/collections.md)
