Базовые типы: int, float и раздельная арифметика

Знакомимся с примитивными типами OCaml и его фирменной особенностью — раздельной арифметикой целых и вещественных чисел.

В OCaml int и float — разные типы, и у них разные операторы: +/* для целых, +./*. (с точкой) для вещественных.

Это первое, что удивляет новичков, и первое, за что OCaml потом благодарят. В большинстве языков + молча работает и для целых, и для дробных, неявно конвертируя одно в другое. Удобно — но именно тут прячутся коварные ошибки: целочисленное деление 5 / 2 = 2 вместо 2.5, потеря точности. OCaml отказался от неявных преобразований полностью.

Примитивные типы

ТипПримерЧто это
int42целое (обычно 63-битное)
float3.14вещественное двойной точности
booltrueлогический
char'a'один символ
string"hi"строка
unit()«ничего», аналог void

Раздельные операторы

let a = 5 + 3        (* int:   8 *)
let b = 5.0 +. 3.0   (* float: 8. *)
let c = 5.0 /. 2.0   (* float: 2.5 *)
let d = 5 / 2        (* int:   2  — целочисленное деление *)

Попытка смешать типы — это ошибка компиляции, а не молчаливое преобразование:

utop # 5 + 3.0 ;;
Error: This expression has type float but an
       expression was expected of type int

Чтобы перейти между типами, нужно явно вызвать float_of_int или int_of_float (последняя усекает дробную часть).

let mix n = float_of_int n +. 0.5
(* mix : int -> float *)

unit — тип «ничего»

Тип unit имеет ровно одно значение — (). Его возвращают функции, выполняемые ради побочного эффекта. Это аналог void, но полноценный тип. Именно поэтому точка входа выглядит как let () = print_string "...": мы говорим, что выражение справа имеет тип unit.

Как работает под капотом

Внутри int хранится в машинном слове с одним битом-тегом, поэтому он 63-битный на 64-битных системах: один бит сборщик мусора использует, чтобы отличать число от указателя. Это делает арифметику int очень быстрой. float же — полноценное 64-битное число IEEE 754, которое в общем случае «упаковано» в куче. Раздельные операторы — следствие отсутствия перегрузки: поскольку + имеет ровно один тип int -> int -> int, для float понадобился отдельный оператор. Так система типов остаётся простой и предсказуемой.

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

  • Написать 2 * 3.14. Нужно 2.0 *. 3.14 — и оператор, и литерал должны быть float.
  • Ждать 2.5 от 5 / 2. Это целые: результат 2.
  • Забыть точку в литерале: 5. и 5.0 — float, а 5 — int.

Итоги

  • int и float — разные типы; смешивать без явного преобразования нельзя.
  • Float-операторы пишутся с точкой: +., -., *., /..
  • unit с единственным значением () — типизированный аналог void.
Проверьте себя
1. Сколько будет `5 / 2` в OCaml?
A2.5
B2
C3
Dошибка компиляции
2. Как сложить два float в OCaml?
Aa + b
Ba +. b
Cadd(a, b)
Da plus b
3. Что произойдёт при вычислении `5 + 3.0`?
AВернётся 8.0
BВернётся 8
CОшибка компиляции из-за несовпадения типов
D3.0 округлится до 3