Reputation: 4909
This is the description of warning 27 from the OCaml manual:
27 Innocuous unused variable: unused variable that is not bound with
let
noras
, 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:
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
Upvotes: 6
Views: 1693
Reputation: 136
For me this warning is useful in order to remind me to explicit more my intention. If we take your example :
fun (x, y) -> x;;
Your intention is to use only the first element. If we rewrite it this way :
fun (x, _ ) -> x;;
You use a pattern matching in the parameter to make your code more concise, but you explain your intention of using only the first element. The added value in this example is small, related to the very simple implementation. But in real life functions, this warning promote a good habit in coding.
Upvotes: 2
Reputation: 35210
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.
Upvotes: 11