4
votes

Right now, I have a nested pattern match that is behaving oddly.

Here is the code snippet

match setting with
| "-f" | "--file" ->            // Open file
    ({ state with file = Some (cur_dir + "\\" + elem) }, "")
| "" ->                         // Option without parameter
    match elem with
    | "-h" | "--help" ->        // Display help message
        ({ state with help = true }, "")
    | "-r" | "--readonly" ->    // Open with readonly
        ({ state with readonly = true }, "")
    | opt -> (state, opt)
| _ -> (state, "")              // Ignore invalid option

Unfortunately, I get two warnings when I compile the above code. One states that the final wildcard pattern is unreachable. The other states that the first match statement is incomplete.

These two warnings seem to be in conflict with each other, and if I add in parentheses to make the expression more clear, the warnings disappear and my code works correctly.

match setting with
| "-f" | "--file" ->            // Open file
    ({ state with file = Some (cur_dir + "\\" + elem) }, "")
| "" ->                         // Option without parameter
    ( match elem with
      | "-h" | "--help" ->        // Display help message
          ({ state with help = true }, "")
      | "-r" | "--readonly" ->    // Open with readonly
          ({ state with readonly = true }, "")
      | opt -> (state, opt) )
| _ -> (state, "")              // Ignore invalid option

My question is this: what was wrong with the original code? It was not a case of a misplaced parenthesis, and it seems like it should be obvious to the compiler where each match ends.

Edit: here's the complete function, in case it helps

let process_args argv =
    let (result, _) = ((default_settings, "-f"), argv) ||>
        Array.fold (fun (state, setting) elem ->
            match setting with
            | "-f" | "--file" ->            // Open file
                ({ state with file = Some (cur_dir + "\\" + elem) }, "")
            | "" ->                         // Option without parameter
                match elem with
                | "-h" | "--help" ->        // Display help message
                    ({ state with help = true }, "")
                | "-r" | "--readonly" ->    // Open with readonly
                    ({ state with readonly = true }, "")
                | opt -> (state, opt)
            | _ -> (state, "")              // Ignore invalid option
        ) in
    result

I should also note that this error occurs with both light and verbose syntax.

Edit2: I mistakenly pasted a slightly altered function, which resulted in some compilation errors instead of just warnings. My apologies.

1
Your example works fine for me. It's almost assuredly an indentation error. - Ringil
The example was copied and pasted straight from the source code. Also, the error occurs regardless of whether or not I use #light "off" - idlys
When I copy your match statement into VS Code, the Ionide plugin shows me no warnings. But when I copy your full process_args function, I get an indentation error on the let statement. I'll write a full answer, but the short version (so you don't have to wait) is: move your ((default_settings, etc. tuple down to the next line and indent it four spaces, and make no other changes, and your code should work. - rmunn
btw, there is Argu if you do a lot argument parsing. - s952163

1 Answers

4
votes

When I copy your full function into VS Code, I get two errors, both caused by the same indentation mistake:

let process_args argv =
    let (result, _) = ((default_settings, "-f"), argv)
        ||> Array.fold (fun (state, setting) elem ->
        // (snip rest of function)

The two errors are on the let of let (result, _) = ..., and on the ||> operator. The error on let is:

Incomplete value or function definition. If this is in an expression, the body of the expression must be indented to the same column as the 'let' keyword.

and the error on the ||> is:

Unexpected infix operator in binding. Expected incomplete structured construct at or before this point or other token.

Both of these errors are actually rather unhelpful in helping you figure out what went wrong, because the actual cause is a simple indentation mistake in your use of the ||> operator.

According to the F# code formatting guidelines, the "pipe" operators (|>, ||>, and so on) needs to be indented at the same level as the expression they're operating on. In your code, the ||> operator is indented under the (result, _) tuple. But that tuple isn't actually what the ||> is meant to be operating on; that tuple is the target of the assignment. The expression that ||> is operating on is the ((default_settings, "-f"), argv) tuple. So you should have indented your code like this:

let process_args argv =
    let (result, _) = ((default_settings, "-f"), argv)
                      ||> Array.fold (fun (state, setting) elem ->
                      // (snip rest of function)

Or else:

let process_args argv =
    let (result, _) =
        ((default_settings, "-f"), argv)
        ||> Array.fold (fun (state, setting) elem ->
        // (snip rest of function)

The second one is better, IMHO.