Вывод типов и явная типизация

Урок о том, как Nim угадывает тип значения самостоятельно и когда лучше подсказать тип явно.

Вывод типов (type inference) — способность компилятора самому определить тип переменной по значению, без того, чтобы вы писали этот тип руками.

Зачем это нужно

Nim — язык со строгой статической типизацией: у каждой переменной есть ровно один тип, и он не меняется по ходу программы (в отличие от Python, где переменная может сначала хранить число, а потом строку). Казалось бы, это должно означать, что тип нужно писать каждый раз руками, как в старых версиях Java или C. Но у Nim есть вывод типов: если из значения справа и так понятно, какой тип нужен, компилятор определяет его сам, и вам не приходится об этом думать.

Это удобное сочетание: код читается почти так же коротко, как в Python, но при этом сохраняются все проверки типов, которые ловят ошибки ещё до запуска программы.

Как выглядит вывод типов на практике

Самый простой способ объявить переменную в Nim — вообще не писать тип, а просто присвоить значение:

let city = "Казань"
let population = 1300000
let averageTemp = 5.8
let isCapital = false

echo city, ": ", population, " человек, ", averageTemp, "°C, столица: ", isCapital

Вывод:

Казань: 1300000 человек, 5.8°C, столица: false

Компилятор смотрит на каждое значение справа от = и делает вывод: "Казань" в кавычках — значит, это string; 1300000 без точки — значит, int; 5.8 с точкой — значит, float; false — значит, bool. Это происходит на этапе компиляции, никакой «магии» во время выполнения программы уже не остаётся — тип каждой переменной зафиксирован заранее.

Как указать тип явно

Синтаксис явного указания типа — через двоеточие после имени переменной, перед знаком равенства:

let temperature: float = 5.8
var attempts: int = 0
let greeting: string = "Привет!"

Смысл тот же, что и без явного типа, но теперь тип виден сразу в объявлении, а не только выводится из значения. Более того, тип и значение можно комбинировать так, чтобы результат отличался от того, что вывелся бы сам по себе:

let score: float = 10

echo score

Вывод:

10.0

Здесь литерал 10 сам по себе выглядит как целое число, но явное указание : float говорит компилятору: «сохрани его как дробное». Nim в этом случае аккуратно преобразует целочисленный литерал в дробный прямо при компиляции.

Когда тип обязательно нужен явно

Бывают ситуации, где Nim не может сам угадать тип, потому что по правой части выражения это неоднозначно. Самый частый пример — когда сначала объявляют переменную, а значение присваивают только позже:

var balance: int
balance = 100

echo "Баланс: ", balance

Вывод:

Баланс: 100

Без начального значения компилятору неоткуда взять подсказку о типе — поэтому здесь тип после двоеточия обязателен. Если его не указать, компилятор Nim выдаст ошибку прямо на этапе сборки:

var balance
balance = 100  # ошибка: тип не определён

Вывод:

Error: type of 'balance' is not known

Явный тип как средство читаемости

Даже когда вывод типов справился бы и сам, иногда явный тип стоит писать нарочно — не для компилятора, а для человека, который потом будет читать код. Например, в объявлении функции тип параметра всегда обязателен — по одному лишь имени параметра компилятору неоткуда узнать тип:

proc describeAge(age: int): string =
  "Возраст: " & $age & " лет"

echo describeAge(15)

Вывод:

Возраст: 15 лет

Здесь age: int — обязательная часть объявления функции (proc), а не подсказка компилятору «для галочки»: без типа параметра компилятор просто не соберёт код, ведь он должен заранее знать, какие операции с этим параметром допустимы.

Частые ошибки

  • Думать, что раз тип не написан явно, значит, его вообще нет. Это не так: тип определяется всегда, просто иногда его пишете вы, а иногда — выводит компилятор. С точки зрения строгости проверок разницы нет.
  • Объявлять переменную без значения и без типа. var x без начального значения и без : тип компилятор не пропустит — неоткуда взять подсказку.
  • Ожидать, что тип может «сам поменяться» по ходу программы. В отличие от Python, если let x = 5 вывелось как int, эта переменная уже никогда не сможет хранить строку или список — тип фиксируется один раз, при объявлении.

Итоги

  • Nim умеет выводить тип переменной по значению справа от = — писать тип руками не обязательно.
  • Явный тип указывается через двоеточие: let x: float = 10.
  • Явный тип обязателен, когда значение не присваивается сразу при объявлении, а также в параметрах функций.
  • Какой бы способ вы ни выбрали, тип переменной фиксируется на этапе компиляции и не меняется дальше.
Проверьте себя
1. Какой тип выведет компилятор Nim для let value = 5.8?
Aint
Bfloat
Cstring
Dbool
2. В каком случае тип переменной в Nim нужно указать явно?
AВсегда, вывод типов в Nim не работает
BКогда переменная объявляется без начального значения
CТолько для переменных с текстом
DТолько внутри циклов