Поделиться через


Автоматическое обобщение (F#)

В языке 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>

См. также

Ссылки

Вывод типа (F#)

Универсальные шаблоны (F#)

Статически разрешаемые параметры типа (F#)

Ограничения (F#)