Автоматическое обобщение

F# использует вывод типов для оценки типов функций и выражений. В этом разделе описывается, как F# автоматически обобщает аргументы и типы функций, чтобы они работали с несколькими типами, когда это возможно.

Автоматическое обобщение

Компилятор F#, когда он выполняет вывод типа для функции, определяет, может ли данный параметр быть универсальным. Компилятор проверяет каждый параметр и определяет, имеет ли функция зависимость от конкретного типа этого параметра. Если это не так, тип выводится как универсальный.

В следующем примере кода показана функция, которую компилятор определяет как универсальную.

let max a b = if a > b then a else b

Тип определяется как 'a -> 'a -> 'a.

Тип указывает, что это функция, которая принимает два аргумента одного и того же неизвестного типа и возвращает значение этого же типа. Одна из причин, по которой предыдущая функция может быть универсальной, заключается в том, что оператор больше чем (>) сам по себе является универсальным. Оператор больше имеет сигнатуру 'a -> 'a -> bool. Не все операторы являются универсальными, и если код в функции использует тип параметра вместе с неуниверсаловой функцией или оператором, этот тип параметра нельзя обобщить.

Так как max является универсальным, его можно использовать с такими типами, как int, floatи т. д., как показано в следующих примерах.

let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3

Однако два аргумента должны иметь один и тот же тип. Сигнатура имеет значение 'a -> 'a -> 'a, а не 'a -> 'b -> 'a. Таким образом, следующий код выдает ошибку, так как типы не совпадают.

// Error: type mismatch.
let biggestIntFloat = max 2.0 3

Функция max также работает с любым типом, поддерживающим оператор больше. Поэтому его можно также использовать в строке, как показано в следующем коде.

let testString = max "cab" "cat"

Ограничение значений

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

Это означает, что компилятор выдает ошибку, если вы пытаетесь скомпилировать код, который недостаточно ограничен определенным типом, но также не является универсальным. Сообщение об ошибке для этой проблемы ссылается на это ограничение на автоматическое обобщение значений как ограничение значения.

Как правило, ошибка ограничения значений возникает либо в том случае, если требуется, чтобы конструкция была универсальной, но компилятору недостаточно сведений для ее обобщения, либо если непреднамеренно опустить достаточное количество сведений о типе в необщительной конструкции. Решение ошибки ограничения значений заключается в предоставлении более четкой информации для более полного ограничения задачи вывода типа одним из следующих способов:

  • Ограничить тип необщичным, добавив явную заметку типа к значению или параметру.

  • Если проблема заключается в использовании необщительной конструкции для определения универсальной функции, такой как композиция функции или неполные примененные аргументы функции, попробуйте переписать функцию как обычное определение функции.

  • Если проблема является слишком сложным выражением, чтобы его можно было обобщить, сделайте его в функцию, добавив дополнительный, неиспользуемый параметр.

  • Добавьте явные параметры универсального типа. Этот параметр используется редко.

В следующих примерах кода показан каждый из этих сценариев.

Случай 1. Слишком сложное выражение. В этом примере список counter должен быть int option ref, но он не определен как простое неизменяемое значение.

let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None

Случай 2. Использование необщимой конструкции для определения универсальной функции. В этом примере конструкция не является генерализацией, так как она включает частичное применение аргументов функции.

let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj

Случай 3. Добавление дополнительного неиспользуемого параметра. Так как это выражение недостаточно просто для обобщения, компилятор выдает ошибку ограничения значений.

let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []

Случай 4. Добавление параметров типа.

let arrayOf10Lists = Array.create 10 []
// Adding a type parameter and type annotation lets you write a generic value.
let arrayOf10Lists<'T> = Array.create 10 ([]:'T list)

В последнем случае значение становится функцией типа, которая может использоваться для создания значений различных типов, например следующим образом:

let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>

См. также раздел