Skip to content

Types

Every value in Tessel has a type, and the type of every variable is known before the program runs. tessel check uses this to catch mistakes, like adding a number to a piece of text, before you ever run your code.

You rarely have to write types yourself: Tessel infers them from the values you use. You do write them for function parameters, return values, struct fields, and empty lists or dictionaries.

TypeExample valuesWhat it holds
Int0, 42, -7, 1_000A whole number (64-bit)
Float0.5, 3.14, 2.0A decimal number (64-bit)
Booltrue, falseA truth value
String"hi", "Hello, {name}"Text
Range0..3, 1..nA range of whole numbers
DataData(text: "hi")Raw bytes, such as a file’s contents
[T][1, 2, 3]A list of values of type T
[K: V]["ada": 36]A dictionary from keys K to values V
Set<T>[1, 2] (where a set is expected)A set of values, each once
(A, B)(1, "one")A tuple: a few values grouped together
T?nil, 5An optional: a T, or nil
fn(A, B) -> R{ a, b in a + b }A function
structs and enumsPoint(x: 1, y: 2), .redTypes you declare yourself

For building user interfaces there are also View and a few built-in enums such as Color, Weight, Alignment and IconName. Those are covered in Building apps.

An Int is a whole number from −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (a 64-bit signed integer).

let year = 2026
let big = 1_000_000
let negative = -40

Int arithmetic never silently wraps around. If a result is too large or too small to fit, the program stops with an integer overflow error. Dividing by zero (with / or %) also stops the program. See Runtime errors.

Dividing two Ints gives an Int and drops the fraction: 7 / 2 is 3.

A Float is a 64-bit floating-point number (a “double” in many languages). A number literal with a decimal point is a Float.

let price = 9.99
let half = 0.5
print(10.0 / 4.0) // 2.5
print(2.0 * 3.0) // 6.0

When a Float is printed or put into a string, a whole value keeps its .0 (so 6.0, not 6), which makes it easy to tell from an Int. To show a fixed number of decimals, use formatted:

let third = 1.0 / 3.0
print(third) // 0.3333333333333333
print(third.formatted(decimals: 2)) // 0.33

Math functions like sqrt, pow, sin and round, and the constant pi, are built in:

let radius = 2.0
let area = pi * pow(radius, 2.0)
print(area.formatted(decimals: 1)) // 12.6
print(sqrt(2.0)) // 1.4142135623730951

See Math for the full list.

Float arithmetic follows the usual floating-point rules: it never stops the program. Dividing by zero gives infinity, which prints as inf. And, as in most languages, some decimal fractions can’t be stored exactly: 0.1 + 0.2 prints 0.30000000000000004.

A Bool is true or false. Comparisons produce Bools, and if conditions need one.

let score = 72
let passed = score >= 50
if passed && score < 80 {
print("Passed")
}

A String is a piece of Unicode text. Strings support interpolation with {…} and escapes such as \n; see Basics.

let name = "Ada"
let greeting = "Hello, {name}!"
print(greeting.count) // 11
print(greeting.uppercase()) // HELLO, ADA!

count counts characters, not bytes, so "héllo".count is 5. The full list of string properties and methods is on the Collections page.

Strings can’t be indexed with […]. Use character(at:) for one character, characters for all of them as a list, or methods like substring, split, contains or hasPrefix.

a..b is a Range: the whole numbers from a up to, but not including, b. You can loop over a range, store it, pass it around, compare two ranges with ==, and print one:

struct Chapter {
title: String
pages: Range
}
let intro = Chapter(title: "Intro", pages: 1..4)
for page in intro.pages {
print("page {page}") // page 1, page 2, page 3
}
print(intro.pages) // 1..4

Data is a sequence of bytes: the contents of a file that isn’t text, or values encoded with toBinary. Make one with Data(), Data(bytes: [72, 105]) or Data(text: "Hi"); data.count is its size and data[i] a byte, as an Int from 0 to 255.

let greeting = Data(text: "Hi")
print(greeting.count) // 2
print(greeting[0]) // 72

See Data in the standard library reference.

[T] is an ordered list whose items all have type T:

let primes = [2, 3, 5, 7] // [Int]
var names: [String] = [] // an empty [String]
names.append("Grace")
print(primes[0]) // 2

[K: V] maps keys of type K to values of type V:

var ages = ["ada": 36, "alan": 41] // [String: Int]
ages["grace"] = 85
print(ages["ada"] ?? 0) // 36

Keys can be Int, String, Bool, an enum whose cases carry no values, or a tuple or struct made of those. Looking up a key gives an optional, because the key might be missing.

Set<T> holds values without order and without repeats, and (A, B) is a tuple, a few values of any types grouped together:

var seen: Set<Int> = [1, 2, 2] // 2 values
seen.insert(3)
let pair = (1, "one") // (Int, String)
print(pair.1) // one

Lists, dictionaries and sets have many methods, and tuples can be taken apart with let (a, b) = pair; see Collections.

T? is “a T, or nothing”. The nothing value is written nil:

var nickname: String? = nil
nickname = "Countess"
print(nickname ?? "no nickname")

You can’t use an optional as if it were a plain value. You unwrap it first with if let, ?? or ?.. See Optionals and Errors.

Functions are values too. Their type is written with fn:

TypeA function that…
fn()takes nothing and returns nothing
fn(Int) -> Booltakes an Int and returns a Bool
fn(String, Int) -> Stringtakes a String and an Int, returns a String
fn() -> Inttakes nothing and returns an Int
let isEven: fn(Int) -> Bool = { n in n % 2 == 0 }
print(isEven(4)) // true

To make a function type optional, the ? goes at the end: fn()? is an optional function. See Closures.

You can declare your own types:

struct Point {
x: Float
y: Float
}
enum Suit { hearts, diamonds, clubs, spades }

See Structs and enums.

Tessel works out a variable’s type from its starting value:

let count = 3 // Int
let price = 9.99 // Float
let names = ["a", "b"] // [String]
let lookup = ["a": 1] // [String: Int]
let isOn = count > 2 // Bool

Write the type after a colon when you want a different one, or when Tessel can’t tell:

let ratio: Float = 1 // a Float, not an Int
var items: [String] = [] // an empty list has no items to look at
var cache: [String: Int] = [:]
var selected: Int? = nil // nil alone doesn't say what's missing

Without the type, an empty list is an error:

var items = []
error: can't tell what type this empty list is
= help: give it a type, like `var names: [String] = []`

Function parameters and return types are always written out. See Functions.

Tessel never converts between types on its own. A variable of type Int is never used as a Float, or turned into text, unless you ask for it. You convert with Int(…), Float(…) and String(…):

ConversionFromResultNotes
Float(x)Int or FloatFloatFloat(7) is 7.0
Int(x)Int or FloatIntDrops the fraction: Int(3.99) is 3, Int(-2.99) is -2
Int(text)StringInt?nil if the text isn’t a whole number
Float(text)StringFloat?nil if the text isn’t a number
String(x)Int, Float, Bool, String or an enumStringSame text as "{x}"
let count = 7
print(Float(count) / 2) // 3.5
print(Int(3.99)) // 3
print(Int("42") ?? 0) // 42
print(Int("forty-two") ?? 0) // 0
print(Float("2.5") ?? 0) // 2.5
print(String(12) + " items") // 12 items

Parsing text can fail, so Int("…") and Float("…") return optionals. Spaces around the number are ignored, so Int(" 12 ") is 12. Int("12.5") is nil, because 12.5 is not a whole number.

Int(x) on a Float that is too large for an Int gives the largest (or smallest) Int instead of stopping the program.

Mixing types by mistake gives an error that suggests the conversion:

let count = 7
let half = count / 2.0
error: can't use `/` on `Int` and `Float`
= help: convert one side with `Float(…)` or `Int(…)`