Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is OCaml's warning 27 "Innocuous unused variable" useful?

Tags:

ocaml

This is the description of warning 27 from the OCaml manual:

27 Innocuous unused variable: unused variable that is not bound with let nor as, and doesn't start with an underscore (_) character.

This warning is turned on by jbuilder --dev, and I'm curious to know in which cases people find it useful. For me, it's an annoyance to get warnings when I write code like this:

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

or like that:

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>

I know that prepending an underscore to the identifiers as in _loc suppresses the warnings, but it's not compatible with my notions that:

  1. variables starting with an underscore are ugly and are meant for use in generated code, hidden from the programmer;
  2. a name given to something should not have to change based on how it's used (including unused).

Using underscores, the code becomes:

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

or

(* 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
like image 827
Martin Jambon Avatar asked May 22 '18 18:05

Martin Jambon


People also ask

What does unused variable mean in C?

Unused variable is a warning, not an error. It comes up when you assign to a variable but don't ever read from it. printf() takes a variable argument list. Since you want to print a number, as you've indicated by the "%d", you need to pass an int after. Otherwise, your program will probably crash: printf("%d\n", i);

Why is unused variable an error?

Unused variable warnings are emitted during compiletime and should be a hint to you as the developer, that you might have forgotten to actually process a value that you have bound to a name. Thats the main reason for the warning.


1 Answers

It is very useful in the monadic code, where instead of the common syntactic let bindings you're forced to use monadic >>= bind operator. Basically, where

let x = something in
code

translates to

something >>= fun x ->
code

If x is not used in code then only with the 27 warning enabled the latter will be highlighted, while the former will produce a warning by default. Enabling this warning, revealed lots of bugs for us. For example, it showed us that this code is buggy :)

Another source of use cases are higher-order functions, i.e., map, fold, etc. It captures one of the most common bugs:

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

Concerning the ugliness, I totally agree that underscores are ugly, but in most cases, this is the main purpose of them - to highlight the suspicious code. Concerning the example, that you're showing, in the modern OCaml it could be easily addressed with the inline records, e.g.,

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

so that instead of using the underscores, you can just omit the unused field,

 let rec eval = function
   | Plus {lhs; rhs} -> eval lhs + eval rhs

You can use the same approach without using inline records by sparing some extra space in your program and defining all those records separately. The real-world example.

like image 106
ivg Avatar answered Nov 11 '22 00:11

ivg