删除警告此表达式在 Ocaml 中应具有类型单元

Remove Warning this expression should have type unit in Ocaml

为什么我会收到警告此表达式应具有此代码的类型单元?虽然它做了它应该做的。

let matchInf42 list =
        let a = ref 0 in
        let lstLength = List.length list in
        let rec matchInf4242 list = 
        match list with
        |[]->[]
        |m::body->
        begin
        if (m < 42) then a := !a + 1;
        matchInf4242 body
        end
        in matchInf4242 list;
        if(!a = lstLength) then -1 else 0

警告:

ocamlopt match.ml -o m
File "match.ml", line 14, characters 7-24:
14 |     in matchInf4242 list;
            ^^^^^^^^^^^^^^^^^
Warning 10: this expression should have type unit.

TL;DR: 您得到的错误是典型的类型错误(大部分)

“大部分”是因为,诚然,这不是“错误”,而只是这里的“警告”,但似乎这种警告 (Warning 10: non-unit-statement) 总是值得解决(即,避免).

它实际上是以下模式的一个实例:

42; print_string "…" ;;

(* or more generally *)
any_value_not_having_type_unit; any_value_having_type_unit ;;

(* which would raise *)
> Characters 0-2:
>   42; print_string "…";;
>   ^^
> Warning 10: this expression should have type unit.
> …- : unit = ()

更多详情

回想一下,unit 是单例类型(只有值 ()),通常选择将 return 类型分配给“return 没有特定值”,但会产生一些副作用。

实际上,序列运算符正如我们所期望的那样“更通用/更灵活”:

# let semicolon i j = i; j ;;
val semicolon : 'a -> 'b -> 'b = <fun>

也就是说,它 不是 semicolon : unit -> 'b -> 'b,因此当 i 不是时,我们得到的代码 i; j 的消息具有类型 unit,只是一个警告,而不是类型错误。

Fixes/workarounds

避免此警告的两种策略:

  1. 要么忽略,靠ignore函数

    # ignore;;
    - : 'a -> unit = <fun>
    # ignore 42; print_string "…";;
    …- : unit = ()
    
  2. 或change/fix计算序列左侧的方式(因此其类型为unit)。

    在你的问题示例的特定情况下,写这个就足够了(唯一的变化用符号 § 表示):

    let matchInf42 list =
         let a = ref 0 in
         let lstLength = List.length list in
         let rec matchInf4242 list = 
         match list with
         |[] -> () (*←§*)
         |m::body->
         begin
         if (m < 42) then a := !a + 1;
         matchInf4242 body
         end
         in matchInf4242 list;
         if(!a = lstLength) then -1 else 0
    

补充说明

最后,为了完整性(即使它不是您问题的明确部分),请注意您考虑的示例函数也可以以更“功能”的风格实现(没有引用或序列,也避免了对预先调用 List.length 函数):

let matchInf42 l =
  if List.for_all (fun m -> m < 42) l
  then -1 else 0

(* or *)

let matchInf42 l =
  if List.fold_left (fun r e -> r && e < 42) true l
  then -1 else 0

改变
matchInf4242 list
要么
ignore (matchInf4242 list)
或者
matchInf4242 list in ().

这使得声明 return ()(即一个单元),这是 ocaml 所期望的。