Find zeroes in a matrix
I would try to decouple the idea of traversing the matrix to collect information about its content and the actual definition of find_zeros. This way you can reuse the same traversal to implement other functions too.

This leads to something like this (I don't assume that the matrix is a square one): indexedTraversal takes a function add and an initial value ini for the accumulator we will use when traversing the matrix mat. add updates the accumulator given the position of the current cell and its content.

type 'a matrix = 'a array array

let indexedTraversal
    (add : int -&gt; int -&gt; 'a -&gt; 'b -&gt; 'b)
    (ini : 'b)
    (mat : 'a matrix)
    : 'b =
  let m = Array.length mat     in
  let n = Array.length mat.(0) in
  let rec aux (i : int) (j : int) (acc : 'b) =
    let acc' = add i j mat.(i).(j) acc in
         if j &lt; n - 1 then aux i (j + 1) acc'
    else if i &lt; m - 1 then aux (i + 1) 0 acc'
    else acc'
  in aux 0 0 ini


(nota: I don't check that m &gt; 0 here so Array.length mat.(0) might raise an error. You can (and should!) add the test to handle that edge case)

You can then implement find_zeros by defining an appropriate testElt and calling indexedTraversal with the empty list as the inital accumulator. Here I call List.rev because otherwise the result is actually the reverse of what you had implemented. If you don't care about the order in which the elements are listed, you can just do away with it.

let find_zeros (m : int matrix) : (int * int) list =
  let testElt i j x ijs = if x == 0 then (i,j)::ijs else ijs
  in List.rev (indexedTraversal testElt [] m)


We can try it on a simple example and it works:

utop # find_zeros [| [|0;1|]; [|1;0|]|];;
- : (int * int) list = [(0, 0); (1, 1)]