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

时间:2018-05-22 18:03:42

标签: ocaml

这是OCaml manual

中警告27的说明
  

27无害的未使用变量:未与letas绑定的未使用变量,并且不以下划线(_)字符开头。

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. 给某事物的名称不应该根据它的使用方式(包括未使用的)而改变。
  3. 使用下划线,代码变为:

    (* 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
    

2 个答案:

答案 0 :(得分:10)

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

let x = something in
code

转换为

something >>= fun x ->
code

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

另一个用例来源是高阶函数,即mapfold等。它捕获了一个最常见的错误:

let bug init = 
   List.fold ~init ~f:(fun acc xs -> 
     List.fold ~init ~f:(fun acc x -> x :: acc))

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

type exp = 
  | Plus of {loc : loc; lhs : exp; rhs: exp}
  | ...

因此,您可以省略未使用的字段

,而不是使用下划线
 let rec eval = function
   | Plus {lhs; rhs} -> eval lhs + eval rhs

您可以在不使用内联记录的情况下使用相同的方法,方法是在程序中节省一些额外空间并分别定义所有这些记录。 real-world示例。

答案 1 :(得分:1)

对我来说,这个警告很有用,以提醒我更明确的意图。如果我们举个例子:

fun (x, y) -> x;;

您的意图是仅使用第一个元素。如果我们用这种方式重写它:

fun (x, _ ) -> x;;

您在参数中使用模式匹配以使代码更简洁,但您解释了仅使用第一个元素的意图。此示例中的附加值很小,与非常简单的实现有关。但在现实生活中,这种警告促进了编码的良好习惯。