F# match pattern discriminator not defined issue

Viewed 233

im in the process of writing a transposing recursive function and i have stopped at a problem. So i want to have a check using match by calling isTable function to verify that the input M is a valid table, however it errors and im not sure how to fix it

let isTable list =
    match List.map List.length list |> List.distinct |> List.length with
    | 1 -> true
    | _ -> false


let rec transpose M = 
    match M with 
    | []::_ -> []
    | (isTable M) -> [] // i want to check here if M is a valid table
    | _ ->  (List.map List.head M::transpose(List.map List.tail M))

error FS0039: The pattern discriminator 'isTable' is not defined.

3 Answers

Active patterns are one approach, but the overhead of adding one just for a single use is not worth it. An easy and uncluttered solution would be to use a when clause:

let rec transpose M = 
    match M with 
    | []::_ -> []
    | _ when isTable M -> []
    | _ ->  (List.map List.head M::transpose(List.map List.tail M))

None of the answers yet show how to turn your case into an Active Pattern. This is particularly useful for (1) readability and (2) reusability of code. Assuming you'd need isTable more than once, this can be beneficial.

/// Active pattern, must start with capital letter.
let (|IsTable|_|) list =
    match List.map List.length list |> List.distinct with
    | [_] -> Some list
    | _ -> None


let rec transpose M = 
    match M with 
    | []::_ -> []
    | IsTable M -> []    // using the active pattern
    | _ ->  
        List.map List.head M::transpose(List.map List.tail M)

As an aside, your isTable function matched over List.length result. A List.length iterates over the whole list and is O(n). Since we're only interested if the result is one item, the above approach will be more efficient, removing at least one iteration from the code.

Try something like

let rec transpose M = 
    match M with 
    | []::_ -> []
    | _ -> match (isTable M)  with
       | true - > [] // i want to check here if M is a valid table
       | _ ->  (List.map List.head M::transpose(List.map List.tail M))

As a matter of programming style I'd recommend adding a data constructor like Table so that you can match on it but this should get things working.

Related