Именованные параметры Ocaml

Пытаемся понять механизм Ocaml для именованных параметров. Я понимаю основы, но в doc показан такой пример:

# let f ~x ~y = x - y;;
val f : x:int -> y:int -> int = <fun>

# let x = 3 and y = 2 in f ~x ~y;;
- : int = 1

Что именно происходит, когда в приложении используется только тильда? Это просто сокращение от ~x:x, похожее на определения? Если да, может кто-нибудь объяснить, почему это:

# ListLabels.fold_left;;
- : f:('a -> 'b -> 'a) -> init:'a -> 'b list -> 'a = <fun>

# let add = (+) and i = 0 
in ListLabels.fold_left ~add ~i [1;2;3];;

производит

- : f:((add:(int -> int -> int) -> i:int -> 'a) ->
   int -> add:(int -> int -> int) -> i:int -> 'a) ->
init:(add:(int -> int -> int) -> i:int -> 'a) -> 'a = <fun>

person scry    schedule 05.08.2012    source источник


Ответы (1)


Этот человек говорит: «Остерегайтесь, что такие функции, как ListLabels.fold_left, тип результата которых является типовой переменной, никогда не будут считаться полностью применимыми».

Вот что происходит в вашем примере. Остерегайтесь, это немного сложно.

# ListLabels.fold_left;;
- : f:('a -> 'b -> 'a) -> init:'a -> 'b list -> 'a = <fun>

это просто классическое использование: ListLabels.fold_left принимает 3 аргумента, а именно функцию с меткой f, инициализатор init и список.

Сейчас в

let add = (+) and i = 0 
in ListLabels.fold_left ~add ~i [1;2;3];;

заявка ListLabels.fold_left ~add ~i [1;2;3] считается неполной (как говорит мужчина). Это означает, что `ListLabels.fold_left сначала получает свой неименованный аргумент [1;2;3] и возвращает функцию типа f:('a -> int -> 'a) -> init:'a -> 'a. Назовем эту функцию foo.

Поскольку вы даете два именованных аргумента, помеченных add и i, тип 'a считается функциональным типом типа add:'c -> ~i:'d -> 'e.

В зависимости от типа переменных add и i тип 'c должен быть int -> int -> int, а 'd должен быть int.

Заменяя эти значения в типе 'a, мы получаем, что тип 'a равен add:(int -> int -> int) -> i:int -> 'e. И заменив это в типе foo (я рад, что есть копипастинг ;-), его тип

f:((add:(int -> int -> int) -> i:int -> 'e)
    -> int
    -> (add:(int -> int -> int) -> i:int -> 'e))
-> init:(add:(int -> int -> int) -> i:int -> 'e)
-> (add:(int -> int -> int) -> i:int -> 'e)

Удалив ненужные скобки и преобразовав альфа (т.е. переименовав) 'e в 'a, мы получим

f:((add:(int -> int -> int) -> i:int -> 'a)
    -> int
    -> add:(int -> int -> int) -> i:int -> 'a)
-> init:(add:(int -> int -> int) -> i:int -> 'a)
-> add:(int -> int -> int) -> i:int -> 'a

Это тип foo. Но помните, что вы передаете в foo два аргумента, помеченных ~add и ~i. Таким образом, значение, которое вы получаете в конце, относится не к типу add:(int -> int -> int) -> i:int -> 'a, а к типу 'a. И весь тип вашего примера, возвращенный компилятором,

f:((add:(int -> int -> int) -> i:int -> 'a)
    -> int
    -> add:(int -> int -> int) -> i:int -> 'a)
-> init:(add:(int -> int -> int) -> i:int -> 'a)
-> 'a
person jrouquie    schedule 06.08.2012
comment
Вау - какой бардак! Это действительно имеет смысл, спасибо вам большое! - person scry; 06.08.2012