Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1. Values and inference

A Pyfun program is built from values you name with let. In Python you would write name = "Ada". Pyfun uses a keyword, let, and the idea is the same: bind a name to a value. The difference is what happens next. A Pyfun let names a value that does not change, so the program reads as a set of definitions rather than a sequence of updates.

let name = "Ada"
let age = 36
let pi = 3.14
let isAdmin = true

print (f"{name} is {age}")
print (f"pi is about {pi}")
print isAdmin

Running this prints:

Ada is 36
pi is about 3.14
True

You never wrote a type. The compiler infers one for every binding: name is a string, age is an int, pi is a float, and isAdmin is a bool. There are no type annotations on let, and that is a deliberate design choice, not a missing feature. You get the safety of static types without writing them out.

Inference is not guessing. The compiler knows enough about each value to reject code that does not fit. Python allows + to mean both numeric addition and string joining, so a mistake there surfaces only when the line runs. Pyfun keeps the two apart and reports the mismatch before any Python is produced:

error: `+` is numeric and does not concatenate strings — use `String.concat a b`
 --> 1:13
  |
1 | let label = "age: " + 36
  |             ^^^^^^^

The compiler saw that "age: " is a string and that + works on numbers, so it stopped. print and f-strings (f"{x}", the same interpolation Python 3.12 uses) are how you observe a value once it is bound. Because a let binding is immutable, there is no statement that overwrites it in place. That capability exists, and it arrives in lesson 10, but the default is a value that stays put.

Exercise

Two baskets hold fruit. Fill the hole so the program adds them and prints the total. Run pyfun check on the starter: the compiler reports the type the hole expects and lists the names in scope that fit. Lesson 9 covers holes in full. For now, read the note and put the right name where ?count sits.

let apples = 4
let oranges = 3
let fruit = apples + ?count
print (f"total fruit: {fruit}")

Expected output:

total fruit: 7

Open in the playground

Show solution
let apples = 4
let oranges = 3
let fruit = apples + oranges
print (f"total fruit: {fruit}")

The hole had type int, and oranges was the binding in scope that made the sum work.