什么时候OCaml的警告27"无害的未使用的变量"有用吗?

Mar*_*bon 6 ocaml

这是OCaml手册中警告27的描述:

27无关的未使用变量:未使用letnor 绑定的未使用变量as,并且不以underscore(_)字符开头.

这个警告被打开了jbuilder --dev,我很想知道人们认为它在哪些情况下有用.对我来说,当我编写这样的代码时,获取警告是一件烦恼:

$ utop -w +27
utop # fun (x, y) -> x;;
Characters 8-9:
Warning 27: unused variable y.
- : 'a * 'b -> 'a = <fun>

或者像那样:

utop # let error loc msg = failwith (loc ^ ": " ^ msg);;
val error : string -> string -> 'a = <fun>
utop # let rec eval = function
| `Plus (loc, a, b) -> eval a + eval b
| `Minus (loc, a, b) -> eval a - eval b
| `Star (loc, a, b) -> eval a * eval b
| `Slash (loc, a, b) ->
    let denom = eval b in
    if denom = 0 then
      error loc "division by zero"
    else
      eval a / denom
| `Int (loc, x) -> x
;;
Characters 33-36:
Warning 27: unused variable loc.
Characters 73-76:
Warning 27: unused variable loc.
Characters 112-115:
Warning 27: unused variable loc.
Characters 287-290:
Warning 27: unused variable loc.
val eval :
  ([< `Int of 'b * int
    | `Minus of 'c * 'a * 'a
    | `Plus of 'd * 'a * 'a
    | `Slash of 'e * 'a * 'a
    | `Star of 'f * 'a * 'a ]
   as 'a) ->
  int = <fun>

我知道将下划线添加到标识符中就像_loc抑制警告一样,但它与我的观念不相符:

  1. 以下划线开头的变量是丑陋的,意味着在生成的代码中使用,对程序员隐藏;
  2. 给某事物的名称不应该根据它的使用方式而改变(包括未使用的).

使用下划线,代码变为:

(* Here we have _loc or loc depending on whether it's used. *)
let rec eval = function
| `Plus (_loc, a, b) -> eval a + eval b
| `Minus (_loc, a, b) -> eval a - eval b
| `Star (_loc, a, b) -> eval a * eval b
| `Slash (loc, a, b) ->
    let denom = eval b in
    if denom = 0 then
      error loc "division by zero"
    else
      eval a / denom
| `Int (_loc, x) -> x

要么

(* Here it can be hard to know what _ stands for. *)
let rec eval = function
| `Plus (_, a, b) -> eval a + eval b
| `Minus (_, a, b) -> eval a - eval b
| `Star (_, a, b) -> eval a * eval b
| `Slash (loc, a, b) ->
    let denom = eval b in
    if denom = 0 then
      error loc "division by zero"
    else
      eval a / denom
| `Int (_, x) -> x

ivg*_*ivg 10

它在monadic代码中非常有用,而不是常见的语法let绑定,你被迫使用monadic >>=绑定运算符.基本上,在哪里

let x = something in
code
Run Code Online (Sandbox Code Playgroud)

翻译成

something >>= fun x ->
code
Run Code Online (Sandbox Code Playgroud)

如果x未使用,code则仅启用27警告,后者将突出显示,而前者将默认生成警告.启用此警告,为我们揭示了许多错误.例如,它向我们展示了这个代码是错误的:)

用例的另一来源是高阶函数,即map,fold等它捕获的最常见的错误之一:

let bug init = 
   List.fold ~init ~f:(fun acc xs -> 
     List.fold ~init ~f:(fun acc x -> x :: acc))
Run Code Online (Sandbox Code Playgroud)

关于丑陋,我完全同意下划线是丑陋的,但在大多数情况下,这是他们的主要目的 - 强调可疑代码.关于您正在展示的示例,在现代OCaml中,可以使用内联记录轻松解决,例如,

type exp = 
  | Plus of {loc : loc; lhs : exp; rhs: exp}
  | ...
Run Code Online (Sandbox Code Playgroud)

因此,您可以省略未使用的字段,而不是使用下划线,

 let rec eval = function
   | Plus {lhs; rhs} -> eval lhs + eval rhs
Run Code Online (Sandbox Code Playgroud)

您可以在不使用内联记录的情况下使用相同的方法,方法是在程序中节省一些额外的空间并分别定义所有这些记录.在真实世界的例子.

  • 哦,天哪,我读你的答案太快了,然后意识到 OCaml *已经* 有内联记录。这件事情让我感到很快乐。 (2认同)