Ввод в формате с принтерами с необязательными аргументами - PullRequest
2 голосов
/ 17 апреля 2019

Я сталкиваюсь с интересным поведением типера OCaml. Типограф, похоже, не принимает принтер с необязательными аргументами.

Когда функция имеет необязательные аргументы, она может быть напечатана как функция без необязательных аргументов.

(** Simple example *)
let (f1 : ?arg : int -> unit -> int) =
  fun ?(arg = 3) () : int -> arg + 5

let f2  : ((unit -> int) -> int) =
  fun f -> f ()

let x : int = f2 f1
(* The type of f1 matches the signature of f2 :
   the optional argument is well discarded. *)

Здесь f1 имеет необязательный аргумент, но f2 f1 хорошо напечатан. Это потому, что (или, по крайней мере, это то, что я понял), сигнатура аргумента f2 охватывает тип f1. Необязательный аргумент просто отбрасывается.

Однако это поведение отклоняется в принтерах, как показано в этом примере.

 (* Data structure *)
type 'a elt = {
  data : int;
  annot : 'a
}

(* Type of printer annotations *)
type 'annot printer = Format.formatter -> 'annot -> unit

(* Default printer prints nothing *)
let (default : 'a printer) = fun fmt _ -> Format.fprintf fmt ""

(* Generic printer for elts *)
let elt_printer
    ?(print_annot : 'a printer = default)
    (fmt : Format.formatter)
    (elt : 'a elt) =
  Format.fprintf fmt "%i(%a)"
    elt.data
    print_annot elt.annot

(* I don't care about printing the annotation *)
let f (elt : _ elt) =
  Format.printf
    "%a"
    elt_printer elt

Это то, что возвращается компилятором для использования elt_printer в вызове fprintf̀:

This expression has type
         ?print_annot:'a printer -> Format.formatter -> 'a elt -> unit
       but an expression was expected of type Format.formatter -> 'b -> unit

Я полагаю, что Типер умудрился вывести это 'b = 'a elt, но не смог отбросить необязательный аргумент.

У меня есть два вопроса относительно этого поведения:

  1. Это ожидаемое поведение типера для второго примера?

  2. Если нет, существует ли стандартный синтаксис, запрещающий использование функций с необязательными аргументами? Например, есть ли способ запретить использование f1̀ в качестве аргумента f2 в первом примере?

Заранее спасибо.

EDIT: Можно принудительно ввести elt_printer без дополнительного аргумента, объяснив его тип.

let f (elt : _ elt) =
  Format.printf
    "%a"
    (elt_printer : _ printer) elt

РЕДАКТИРОВАТЬ 2: Для помеченных аргументов типизатор строго блокирует преобразования типов, поскольку аргументам должно быть указано конкретное имя. Это не проблема, поднятая в моем вопросе.

1 Ответ

3 голосов
/ 17 апреля 2019
  1. Да, это ожидаемо, функция ?foo -> bar -> baz не может быть преобразована в функцию bar -> baz.Его можно применять только без foo, что сильно отличается от неявного преобразования

  2. Есть и другие решения

Во-первых, выможет «не применять» метку: f ?print_annot:None сообщит типографу, что параметр print_annot из f следует считать отсутствующим.

Во-вторых, и эту технику я использовал для tyxml ( см. здесь ), вы можете добавить аргумент модуля:

val pp : 
  ?encode:(string -> string) ->
  ?indent:bool ->
  ?advert:string ->
  unit -> 
  Format.formatter -> doc -> unit

Тогда у пользователей будет код, похожий на этот:

let s = Format.asprintf "%a" (Tyxml.Html.pp ()) my_html
...