OCaml Printf.sprintf

Nic*_*ner 11 printf ocaml type-systems

为什么会出现这种情况?

# Printf.sprintf ("Foo %d %s") 2 "bar";;
- : string = "Foo 2 bar"

# Printf.sprintf ("Foo %d" ^ " %s") 2 "bar";;
  Printf.sprintf ("Foo %d" ^ " %s") 2 "bar";;
Error: This expression has type string but an expression was expected of type
         ('a -> 'b -> 'c, unit, string) format =
           ('a -> 'b -> 'c, unit, string, string, string, string) format6
Run Code Online (Sandbox Code Playgroud)

我希望首先评估字符串连接,所以一切都会正常进行.这与Printf采用的类型系统技巧有关吗?

Jef*_*eld 18

是的,它与类型系统欺骗有关.如果要创建格式字符串,则需要使用(^^)运算符:

# Printf.sprintf ("Foo %d" ^^ " %s") 2 "bar";;
- : string = "Foo 2 bar"
Run Code Online (Sandbox Code Playgroud)

我没有深入学习这个技巧,但我相信如果打字上下文需要它,编译器愿意将字符串常量提升为printf格式.但是,结果("Foo %d" ^ " %s")不是字符串常量,因此不会被提升.(^^)运算符创建一个类型上下文,如果它们是字符串常量,则可以提升两个操作数.

您可以看到为什么它必须是一个字符串常量:否则无法确定相关类型(要打印的值).

  • @gasche:基本上,您可以将OCaml中的字符串文字视为"重载"; 它可以是字符串值或格式字符串值.类型检查器在编译时确定哪一个.将字符串文字直接赋给`printf`会导致它被推断为格式字符串.将它赋予`^`会使其被推断为字符串.`^`也返回一个字符串,这是`printf`的错误类型,这就是你得到错误的原因. (2认同)

win*_*zki 10

问题比^运营商更广泛地发生.基本上,OCaml编译器需要知道您的格式字符串是文字字符串,并且需要在编译时知道文字字符串.否则,OCaml无法在编译时将您的字符串转换为此BLAHBLAH format6类型.该Printf模块仅适用于在编译时完全已知的格式字符串,或者已使用已转换为该BLAHBLAH format类型的格式字符串.

通常,您可以通过使用^^运算符并在代码中使用这些字符串之前将所有文字字符串显式地转换为BLAHBLAH format类型来解决此问题.

这是另一个例子:

  # Printf.sprintf (if true then "%d" else "%d ") 2;;
  Error: This expression has type string but an expression was expected of type
     ('a -> 'b, unit, string) format =
       ('a -> 'b, unit, string, string, string, string) format6
  (* define a type abbreviation for brevity *)
  # type ('a,'b) fformat = ('a ->'b, unit, string) format;;
  type ('a, 'b) fformat = ('a -> 'b, unit, string) format
  # Printf.sprintf (if true then ("%d":('a,'b)fformat) else ("%d ":('a,'b)fformat)) 2;;
  - : string = "2"
Run Code Online (Sandbox Code Playgroud)

OCaml系统无法识别if ... then "a" else "b"可以转换为BLAHBLAH format.如果你自己编译每个文字字符串BLAHBLAH format,那么一切正常.(注意:如果您尝试将整个转换if/then/elseBLAHBLAH format,则它不起作用,因为OCaml无法验证您的字符串是否为文字.)

问题的起源是类型安全的要求:OCaml的要求有正确的类型,为每个的参数%d%s等,并保证这在编译时.Printf除非在编译时已知整个格式字符串,否则无法保证类型安全.因此,不可能使用Printf通过复杂算法计算的格式字符串,例如,通过选择%s%d随机.

当我们if/then/else用来计算格式字符串,然后OCaml的东西,哦,这是一个复杂的算法,并且在编译时验证类型安全是没有希望的.该^^运营商知道BLAHBLAH format类型和连接格式字符串时产生正确的结果.但是if/then/else不知道BLAHBLAH format,并且没有内置的替代品if/then/else(但我想你可以自己定义这样的东西).