Алекс Каскет
Вывод типов — это одна из функций F#, которая становится любимой у новых программистов. На практике (и очень кратко) это заставляет нас писать меньше типовых аннотаций. Например, со следующим кодом:
пусть х = 42
пусть правда = правда
Компилятор делает вывод, что «x» — это целое число, а «true» — логическое значение. В этом случае алгоритм определения типа определяет тип по значениям, расположенным справа. Но на этом алгоритм не останавливается, он может вывести тип аргументов и возвращаемых значений функции из тела функции. В этом примере:
пусть Истина n =
если n › 42, то верно
иначе ложно
Аргумент «n» используется при сравнении с целым числом, и функция возвращает логические значения, поэтому компилятор может легко сделать вывод, что функция принимает целое число и возвращает логическое значение. Но теперь давайте посмотрим на другой фрагмент кода:
добавим p q = p + q
В этом случае компилятор делает вывод, что функция принимает и возвращает целое число. Что является причиной этого? По сути, компилятор F#, в отсутствие какой-либо другой информации, выводит целое число как тип арифметического выражения. Ключевым в приведенном выше предложении является «любая другая информация», потому что, если, например, у нас есть объявление функции, и мы используем его следующим образом:
добавим p q = p + q
пусть результат = добавить 2,0 40,0
Сигнатура функции «добавить» меняется на «float -> float», потому что на этот раз компилятор выводит тип из первого использования функции. Вопрос в этой ситуации может заключаться в следующем: как мне создать функцию, которая может работать с целыми числами, числами с плавающей запятой или длинными числами?
Мы можем использовать ключевое слово 'inline' для определения встроенной функции, в которой код встроен в сайт вызова во время компиляции.
пусть встроенная сумма x y = x + y
В этой ситуации тип аргументов функции «сумма» статически разрешается, и сигнатура функции становится такой:
val встроенная сумма:
x: ^a -> y: ^b -> ^c
когда ( ^a или ^b) : (статический член ( + ) : ^a * ^b -> ^c)
Мы видим, что перед типом параметров стоит знак вставки (^), указывающий тип, который заменяется фактическим типом во время компиляции, а не во время выполнения. Предложение «когда» указывает ограничения, указывающие на то, что параметр типа должен предоставлять определенные методы, которые можно вызывать из тела функции. В этом случае тип должен предоставлять оператор суммы.
Когда функция не зависит от типа параметра, алгоритм вывода склонен рассматривать параметр как универсальный. Это то, что называется автоматическим обобщением. Например:
давайте сравним p q =
если p ‹ q, то -1
elif p › q тогда 1
иначе 0
В этом случае сигнатура этой функции:
val compare : p:’a -> q:’a -> int, когда ‘a : сравнение
Это указывает на то, что эта функция принимает два аргумента одного типа и возвращает значение того же типа, а также должна поддерживать сравнение.
Однако во многих случаях компилятор не может определить тип. Например, следующий код возвращает ошибку, поскольку компилятор не может определить тип.
List.sortBy(fun x -> x.Length) ["три"; "два"; "один"]
Но если мы перепишем код так:
["три"; "два"; «один»] |› List.sortBy (fun x -> x.Length)
Теперь компилятор знает о типах и может обрабатывать их перед вычислением правой части оператора. Таким образом, мы избегаем использования типов аннотаций, чего мы всегда должны добиваться с помощью функциональных языков, таких как F#.
Вывод
Вывод типа — это мощная функция, предоставляемая F#, которая помогает нам писать меньше аннотаций типов во многих контекстах, таких как аргументы функций, возвращаемые типы, универсальные типы и т. д. Важно знать, как работает алгоритм вывода, чтобы определить тип значения или функция: как функции взаимодействуют и используются, какие ограничения применяются к типу, и если нет, то как и когда происходит обобщение на дженерики.
Чтобы узнать больше о F#, ознакомьтесь с курсами:
https://fsharp.tv/courses/
включая БЕСПЛАТНЫЙ вводный курс, который поможет вам начать работу с функциональным программированием.