Есть несколько понятий, которые вы должны понять смысл этого типа подписи, и я не знаю, какие из них вы уже делаете, так что я старался изо всех сил, чтобы объяснить все важные концепции:
Карринг
Как известно, если у вас есть тип foo -> bar
, это описывает функцию, принимающую аргумент типа foo
и возвращающий результат типа bar
. Так как ->
является правильным ассоциативным, тип foo -> bar -> baz
совпадает с foo -> (bar -> baz)
и поэтому описывает функцию, принимающую аргумент типа foo
и возвращающий значение типа bar -> baz
, что означает, что возвращаемое значение является функцией, принимающей значение типа bar
и возвращающее значение типа baz
.
Такая функция может быть названа, как my_function my_foo my_bar
, который из-за применение функции лево-ассоциативно, является таким же, как (my_function my_foo) my_bar
, т.е. применяется my_function
к аргументу my_foo
, а затем применяет функцию, которая возвращается в результате к аргументу my_bar
.
Поскольку его можно так называть, функцию типа foo -> bar -> baz
часто называют «функцией, принимающей два аргумента», и я сделаю это в оставшейся части этого ответа.
Тип переменных
Если определить функцию как let f x = x
, он будет иметь тип 'a -> 'a
. Но 'a
на самом деле не является типом, определенным в стандартной библиотеке OCaml, так что это?
Любой тип, начинающийся с '
, является так называемой переменной . Переменная типа может соответствовать любому возможному типу. Таким образом, в приведенном выше примере f
можно вызвать с помощью int
или string
или list
или вообще ничего - это не имеет значения.
Кроме того, если переменная того же типа появляется в сигнатуре типа более одного раза, она будет стоять за один и тот же тип. Таким образом, в приведенном выше примере это означает, что возвращаемый тип f
совпадает с типом аргумента. Поэтому, если f
вызывается с int
, он возвращает int
. Если он вызывается с string
, он возвращает string
и так далее.
Таким образом, функция типа 'a -> 'b -> 'a
может принимать два аргумента любых типов (которые могут быть не одного типа для первого и второго аргументов) и возвращает значение того же типа, что и первый аргумент, в то время как функция типа 'a -> 'a -> 'a
будет принимать два аргумента одного типа.
Одна заметка о выводе типа: Если вы явно не указали функцию подписи типа, OCaml всегда будет выводить наиболее общий тип, который вам подходит. Поэтому, если функция не использует какие-либо операции, которые работают только с данным типом (например, +
), предполагаемый тип будет содержать переменные типа.
Теперь, чтобы объяснить тип ...
val something : ('a -> 'b -> 'c) -> ('a -> 'd -> 'b) -> 'a -> 'd -> 'c = <fun>
Этот тип подписи говорит вам, что something
функция принимает четыре аргумента.
Тип первого аргумента - 'a -> 'b -> 'c
. То есть функция, принимающая два аргумента произвольных и, возможно, разных типов и возвращающих значение произвольного типа.
Тип второго аргумента - 'a -> 'd -> 'b
. Это снова функция с двумя аргументами. Здесь важно отметить, что первый аргумент функции должен иметь тот же тип, что и первый аргумент первой функции, а возвращаемое значение функции должно иметь тот же тип, что и второй аргумент первой функции.
Тип третьего аргумента: 'a
, который также является типом первых аргументов обеих функций.
И, наконец, тип четвертого аргумента - 'd
, который является типом второго аргумента второй функции.
Возвращаемое значение будет иметь тип 'c
, то есть тип возврата первой функции.
Заметка о терминологии (которая может помочь в поиске литературы): «подписи» в Ocaml обычно означают что-то другое, а именно аналоговые типы, но для модулей, а не для основных выражений и значений. То, о чем вы спрашиваете, иногда называют «сигнатурой типа», но часто просто «type» или «type scheme», когда есть переменные. – Gilles