Search code examples
f#functional-programmingpattern-matchingdiscriminated-unionguard-clause

Simplifying nested pattern matching F#


I am writing a simple expressions parser in F# and for each operator I want only to support a certain number of operands (e.g. two for Modulo, three for If). Here is what I have:

type Operator =
    | Modulo
    | Equals
    | If

let processOperator operands operator =
    match operator with
    | Modulo ->
        match operands with
        | [ a:string; b:string ] -> (Convert.ToInt32(a) % Convert.ToInt32(b)).ToString()
        | _ -> failwith "wrong number of operands"
    | Equals ->
        match operands with
        | [ a; b ] -> (a = b).ToString()
        | _ -> failwith "wrong operands"
    | If ->
        match operands with 
        | [ a; b; c ] -> (if Convert.ToBoolean(a) then b else c).ToString()
        | _ -> failwith "wrong operands"

I would like to get rid of or simplify the inner list matches. What is the best way to accomplish this? Should I use multiple guards ?


Solution

  • Fold in the operands matching:

    let processOperator operands operator =
        match operator, operands with
        | Modulo, [a; b] -> (Convert.ToInt32(a) % Convert.ToInt32(b)).ToString()
        | Equals, [a; b] -> (a = b).ToString()
        | If, [ a; b; c ] -> (if Convert.ToBoolean(a) then b else c).ToString()
        | _ -> failwith "wrong number of operands"
    

    Better yet, if you can, change the datatype to the following.

    type Operator =
        | Modulo of string * string
        | Equals of string * string
        | If of string * string * string
    

    Then in the match, you can no longer fail.