pick: 'a -> 'a -> bool -> int -> int = <fun>
map2: ('a->'b->'c) -> 'a list -> 'b list -> 'c list
То, что вы видите здесь, - это процесс в функциональном программировании, называемый curry . Чтобы понять это, давайте рассмотрим более простой пример. Допустим, у вас есть функция f
, которая принимает 2 аргумента X
и Y
и выводит Z
. Как мы обычно пишем это
f(X, Y) = Z
Давайте посмотрим на это по-другому - у нас есть функция f
, и если мы дадим ей X
, а затем Y
, она даст нам Z
. Что произойдет, если мы дадим f
1 аргумент X
? Давайте проверим это!
let plus a b = a + b;;
Этот код определяет функцию plus
, которая принимает 2 аргумента a
и b
и возвращает их сумму. Если вы введете plus 1 1;;
в utop
, вы получите 2
. Теперь вывод при вводе
plus 1;;
это
- : int -> int = <fun>
Это означает, что plus(1)
фактически производит ФУНКЦИЮ, которая принимает int
и выводит int
! Подождите минуту, изначально у нас есть функция, которая производит целое число, и вдруг эта же функция производит ... FUNCTION? Что происходит?
Ключевой идеей здесь является представление о функции как о процессе, который использует аргументы один за другим . В духе этого, функция plus
выше похожа на процесс, который потребляет 2 аргумента: если вы дадите ему только 1 аргумент, он остановится и будет ждать 2 аргумента. И этот остановленный процесс в точности аналогичен процессу, который потребляет 1 аргумент: передайте ему оставшийся ингредиент, и он начнет размолоть, чтобы получить ожидаемый результат.
Чтобы увидеть, как эта перспектива может помочь вам понять неясный способ написания сигнатуры функции в вашем примере, давайте посмотрим на функцию pick
:
let pick n m =
if n > m then (fun b x -> if b then x+1 else x*2)
else (fun b x -> if b then x+2 else x*4);;
pick
принимает 2 аргумента, n
и m
, и выводит функцию f
, которая принимает 2 аргумента b
и x
. Определение f
зависит от сравнения.
Если n > m
, то выводится функция fun b x
, определение которой if b then x+1 else x*2
.
Иначе, он выводит функцию fun b x
, определение которой if b then x+2 else x*4
.
Если бы мы написали приблизительную сигнатуру для pick
на основе вышеизложенного понимания, это было бы что-то вроде:
pick: (n, m) -> (function f that takes in b and x, and output some number)
В свете нашего понимания карри pick
похоже на процесс, который потребляет n
, а затем m
. Таким образом, подпись также может быть написана так:
pick: n -> m -> (function f that takes in b and x, and output some number)
О, эй, эту функцию f
можно также рассматривать как процесс, который потребляет b
, а затем x
, поэтому мы можем также написать:
pick: n -> m -> (b -> x -> some number)
, который выглядит поразительно похожим на:
pick: 'a -> 'a -> bool -> int -> int = <fun>
Теперь, как, черт возьми, OCaml знает, что b
должно быть bool
, а x
и some number
- int
, в OCaml есть функция с именем type умозаключение . По сути, компилятор смотрит на операции, которые вы выполняете над переменными, и пытается угадать их типы. Например. Я вижу if b
в коде, поэтому b
должно быть bool
.
В итоге , этот неясный способ написания сигнатуры функции называется curry , и как OCaml узнает, что b
является bool
, через функцию, называемую вывод типа в OCaml. Это должно облегчить поиск.