F # pattern matching for C # classes

I have a C # module that receives and processes various instances of the Operation class. The interface declares one method:

Operation Transform(Operation o1, Operation o2);

      

But there are several types of operations. For example, there are InsertOperation and DeleteOperation functions for simple text editing, so the body of the Transform method starts by sorting which operations it received and casting them. After learning some F #, I wanted to rewrite this part of the project in it (as a practice and experiment), and thought that I could handle it much better with a pattern like this:

let Transform (oa: Operation) (ob: Operation) = 
    match oa, ob with 
    | InsertOperation o1, InsertOperation o2 -> //transformation
    | DeleteOperation o1, InsertOperation o2 -> //transformation
    | InsertOperation o1, DeleteOperation o2 -> //transformation
    | DeleteOperation o1, DeleteOperation o2 -> //transformation

      

However, for this I got the following error message:

The pattern discriminator 'InsertOperation' is not defined

      

The Operation class and its descendants are written in C #, but I thought it shouldn't be a problem. Can someone please explain why this is a problem and how can I get around it?

+3


source to share


2 answers


Since this is not a discriminatory union, but rather a collection of classes (written in C #), you will need to use a test pattern like:

let Transform (oa: Operation) (ob: Operation) = 
    match oa, ob with 
    | (:? InsertOperation as o1), (:? InsertOperation as o2) -> //transformation
    | (:? DeleteOperation as o1), (:? InsertOperation as o2) -> //transformation
    | (:? InsertOperation as o1), (:? DeleteOperation as o2) -> //transformation
    | (:? DeleteOperation as o1), (:? DeleteOperation as o2) -> //transformation

      



For more information, see Generic Test Templates in Pattern Matching .

+6


source


Another answer assumes a direct type match in your function Transform

, which is a great solution. However, you can drop some of the pattern if you define your own active pattern for your case, especially since it seems that you will match against Operation

types more than once.

let (|Insert|Delete|Unknown|) (oper: Operation) =
    match oper with
    | :? InsertOperation as iop -> Insert iop
    | :? DeleteOperation as dop -> Delete dop
    | _ -> Unknown // catches other subtypes of Operation you don't know about yet.

      

And you can use it like this:



let transform (oa: Operation) (ob: Operation) = 
    match oa, ob with
    | Insert oa, Insert ob -> ...
    | Delete oa, Insert ob -> ...
    ...
    | Unknown, _ | _, Unknown -> ...

      

This gives you pretty much the same syntax you would have with discriminatory union, but more importantly, it allows you to think of your operations as if it were a private type for pattern matching purposes. You will receive correct warnings for cases that you did not handle, for example, instead of a generic "other subtype" warning.

+4


source







All Articles