使用带有可选参数的打印机输入格式
Typing in Format with printers with optional arguments
我遇到了 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
这是编译器在 fprintf̀
调用中使用 elt_printer
返回的内容:
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
,但未能丢弃可选参数。
关于此行为我有两个问题:
这是第二个示例的预期行为吗?
如果没有,是否有禁止使用带有可选参数的函数的标准语法?例如,有没有办法禁止在第一个示例中使用 f1̀
作为 f2
的参数?
提前谢谢你。
编辑:
可以通过明确其类型来强制键入不带可选参数的 elt_printer
。
let f (elt : _ elt) =
Format.printf
"%a"
(elt_printer : _ printer) elt
编辑 2:
对于带标签的参数,typer 严格阻止类型转换,因为必须为参数提供特定名称。这不是我问题中提出的问题。
是的,这是意料之中的,函数?foo -> bar -> baz
不能转换为bar -> baz
的函数。它只能在没有 foo
的情况下应用,这与隐式转换有很大不同
有两种解法
首先,你可以"not apply"标签:f ?print_annot:None
会通知输入者f
的参数print_annot
应该被认为是不存在的。
其次,这是我用于tyxml的技术(see here),您可以添加一个单位参数:
val pp :
?encode:(string -> string) ->
?indent:bool ->
?advert:string ->
unit ->
Format.formatter -> doc -> unit
然后用户将拥有如下代码:
let s = Format.asprintf "%a" (Tyxml.Html.pp ()) my_html
我遇到了 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
这是编译器在 fprintf̀
调用中使用 elt_printer
返回的内容:
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
,但未能丢弃可选参数。
关于此行为我有两个问题:
这是第二个示例的预期行为吗?
如果没有,是否有禁止使用带有可选参数的函数的标准语法?例如,有没有办法禁止在第一个示例中使用
f1̀
作为f2
的参数?
提前谢谢你。
编辑:
可以通过明确其类型来强制键入不带可选参数的 elt_printer
。
let f (elt : _ elt) =
Format.printf
"%a"
(elt_printer : _ printer) elt
编辑 2: 对于带标签的参数,typer 严格阻止类型转换,因为必须为参数提供特定名称。这不是我问题中提出的问题。
是的,这是意料之中的,函数
?foo -> bar -> baz
不能转换为bar -> baz
的函数。它只能在没有foo
的情况下应用,这与隐式转换有很大不同有两种解法
首先,你可以"not apply"标签:f ?print_annot:None
会通知输入者f
的参数print_annot
应该被认为是不存在的。
其次,这是我用于tyxml的技术(see here),您可以添加一个单位参数:
val pp :
?encode:(string -> string) ->
?indent:bool ->
?advert:string ->
unit ->
Format.formatter -> doc -> unit
然后用户将拥有如下代码:
let s = Format.asprintf "%a" (Tyxml.Html.pp ()) my_html