Search code examples
ocamlmutability

Why this reference changed without been assigned new value?


I'm using ocaml 4.10.0 and have a function defined like this:

type t = (string * bool) list

let test (t:t) : bool * (string * bool) list =
  match t with
  | [] -> (true,[])
  | (s,b)::[] -> (true,t)
  | _ ->
     let rb = ref true in
     let rl = ref t in
     let rec func (t':t) : unit = 
        match t' with
        | [] -> ()
        | (s,b)::tl -> 
           List.iter(
              fun (s',b') -> 
                 if ((s=s') && (b<>b')) then 
                    rb := false;
                    rl := [(s,b);(s',b')]
           ) tl;
           if (!rb<>false) then func tl;
     in func t; 
     (!rb,!rl)

This function should test a list of (string * bool) and return (true,original list) if no inconsistent (string * bool) pairs, otherwise (false,inconsistent pairs).

For example:

test [("a",true);("b",false);("a",true)] should return (true,[("a",true);("b",false);("a",true)])

test [("a",false);("b",false);("a",true)] should return (false,[("a",false);("a",true)])

However the function I have now is partially wrong, which will return:

test [("a",true);("b",false);("a",true)] returns (true,[("b",false);("a",true)])

test [("a",false);("b",false);("a",true)] returns (false,[("a",false);("a",true)])

I can't figure out why rl will change while the if statement isn't called.


Solution

  • You have this:

    if ((s=s') && (b<>b')) then 
       rb := false;
       rl := [(s,b);(s',b')]
    

    But your indentation is misleading. The then only controls one following expression. In other words, the third line here is always executed.

    You can use begin/end (or parentheses) to group both expressions inside the then:

    if ((s=s') && (b<>b')) then
       begin
       rb := false;
       rl := [(s,b);(s',b')]
       end
    

    This is a reason that some people suggest you use a system that indents your code automatically. You're less likely to end up with misleading indentations that way.