Match inside match - ocaml raises syntax error

Does anyone know why this function raises the syntax error? I haven't provided my written side functions, since they are probably not that relevant here, since it's revolving around proper syntax.

I tried deleting the brackets that raised the error (which I think.. should be there?), only to then raise another syntax error one line lower, at the begining of the row with the line "|".

type 'a grid = 'a Array.t Array.t

type problem = { initial_grid : int option grid }

type available = { loc : int * int; possible : int list }

type state = { problem : problem; current_grid : int option grid; available = available list } 

let branch_state (state : state) : (state * state) option =
  if prazni_kvadratki state.current_grid = [] then
    None
  else
    let lst = prazni_kvadratki state.current_grid in  
    let loc = List.hd lst in
    let st1_grid = copy_grid state.current_grid in
    let st2_grid = copy_grid state.current_grid in
    match razpolozljive state.current_grid loc with
    | x :: xs -> (vstavi_vrednost st1_grid loc (Some x);
                  let st1 = {problem = state.problem; current_grid = st1_grid} in
                  match xs with
                  | [y] -> (vstavi_vrednost st2_grid loc (Some y);
                           let st2 = {
                             problem = state.problem; 
                             current_grid = st2_grid
                           })   (* this is where it shows me a syntax error*)
                  | y :: ys -> let st2 = {
                      problem = state.problem; 
                      current_grid = copy_grid state.current_grid; 
                      available = {loc = loc; possible = xs}
                    })
Some (st1, st2)

Solution 1:

On around the 5th last line or so you have let with no matching in. The let expression always must have an in.

The basic rule for nested match is that you should use parentheses or begin/end around the inner one:

match x with
| [] -> 0
| [_] ->
   begin
   match y with
   | [] -> 1
   | _ -> 2
   end
| _ -> 3

Otherwise the final cases of the outer match look like they belong to the inner one. I don't think this is your problem here because you have no outer cases after the inner match.

Solution 2:

Syntax issues

You have a few syntax issues.

type state = { problem : problem; current_grid : int option grid; available = available list }

You likely meant to have:

type state = { problem : problem; current_grid : int option grid; available : available list }

However, given how you construct values later in your program where you provide a value for the available field in one case but not in the other, you may want a variant type that allows your state type to be constructed with or without this value, with distinct behavior when not constructed with this value. This might look like:

type state = 
| With_available of { problem : problem; 
                      current_grid : int option grid; 
                      available : available list }
| Without_available of { problem : problem; 
                         current_grid : int option grid }

The other syntax issue is missing an in to go with a let which brings us to:

Scoping issues

There are clearly some miunderstandings here for you in regards to how scope works with let bindings in OCaml.

Aside from a definition at the topmost level of a program, all let bindings are local bindings. That is, they apply to a single expression that trails an in keyword.

Consider this toplevel session.

# let x = 5;;
val x : int = 5
# let y =
    let x = 42 in
    x + 3;;
val y : int = 45
# x;;
- : int = 5
#

Here the x bound with let x = 42 in x + 3 is only in scope for the duration of the expression x + 3. Once we're done with that expression, that binding for x is gone. In the outer scope, x is still bound to 5.

In both cases in your match you bind names st1 and st2, which would have to be local bindings, but then you try to use them in an outer scope, where they don't exist.

If you want st1 and st2, you'd need to bind them in a similar way to a and b in the below simple example.

# let (a, b) = match [1; 2; 3] with
  | [x] -> (x, x)
  | x :: y :: _ -> (x, y)
  | _ -> (1, 1)
  in
  a + b;;
- : int = 3
#

Pattern-matching

Please also note that the pattern-matching you're shown is not exhaustive. It does not handle an empty list. If you consider it impossible that an empty list will be a result, you still have to either handle it anyway or use a different data structure than a list which can by definition be empty.

You've shown pattern-matching of the basic pattern:

match some_list with
| x :: xs -> 
  match xs with 
  | [y] -> ...
  | y :: xs -> ...

We can actually match against the two possibilities you've show in one level of match.

match some_list with
| x :: [y] -> ...
| x :: y :: ys -> ...

If you still need to address y :: ys as xs in the second case, we can readily bind that name with the as keyword.

match some_list with
| x :: [y] -> ...
| x :: (y :: ys as xs) -> ...