Автоматическое обобщение
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>