Комбинация специального трюка OCaml для printf
и значения полиморфизма.
Как вы, возможно, знаете, Printf.printf
не принимает string
, но тип данных format
. OCaml типа проверка имеет специальное правило для ввода строковых литералов для printf
: если оно введено в format
если контекст просит:
# "%d";;
- : string = "%d"
# ("%d" : _format);;
- : (int -> 'a, 'b, 'a) format = ...
система типа OCaml имеет еще один трюк называется значение полиморфизма (точнее, расслаблено значение полиморфизм). Его цель - правильно набирать выражения с побочными эффектами. Я не объяснить его деталь, но он ограничивает полиморфизм: некоторые формы выражения называется «экспансивным» не может иметь полиморфные типов:
# fun x -> x;;
- : 'a -> 'a = <fun>
# (fun x -> x) (fun x -> x)
- : '_a -> '_a = <fun>
В выше, (fun x -> x) (fun x -> x)
не имеет полиморфный типа, в то время как функция тождества fun x -> x
имеет. Это связано с формой выражения (fun x -> x) (fun x -> x)
: он «экспансивный». Странная переменная типа '_a
является переменной мономорфного типа: она может быть создана для некоторого типа только один раз. С другой стороны, полиморфные переменные, такие как 'a
, могут быть созданы для разных типов для каждого использования.
Давайте вернемся к коду:
# let (a, p) = (2, Printf.printf);;
val a : int
val p : ('a, out_channel, unit) format -> 'a
Здесь p
имеет полиморфный тип ('a, out_channel, unit) format -> 'a
. 'a
может быть экземпляр более чем для одного типа, поэтому p "abc"; p "%d" 3
является типичным: полиморфный тип может быть создан для (unit, out_channel, unit) format -> unit
для первого использования p
и (int -> unit, out_channel, unit) format -> int -> unit
для второго использования p
.
После того, как вы изменить константу 2
к 2+2
, что экспансивный, все выражение становится экспансивной тоже, и набрав изменения:
# let (a, p) = (2+2, Printf.printf);;
val a : int
val p : ('_a, out_channel, unit) format -> '_a
Здесь p
не имеет больше не полиморфный переменные 'a
но мономорфический '_a
. Эта мономорфная переменная унифицирована (создана) до unit
при первом использовании p
, и в результате p
тип становится (unit, out_channel, unit) format -> unit
. Это может принимать только один аргумент, поэтому ввод второго использования p
с 2 аргументами не выполняется.
Один простой способ избежать этой ситуации, чтобы разделить свое определение на два:
let a = 2 + 2 in
let p = Printf.printf in
p "abc"; p "%d" 3
Я также заинтересован в ответ, это на первый взгляд странно. –