Search code examples
f#discriminated-unionactive-pattern

Subset of Union members as "parameter" in Pattern matching


Let us have a type definition for a tree with several types of binary nodes, among other types of nodes, i.e.

type Tree =
| BinaryNodeA of Tree * Tree
| BinaryNodeB of Tree * Tree
| [Other stuff...]

I want to manipulate this tree using a recursive function that could, e.g., swap subnodes of any kind of binary node (by constructing a new node). The problem that is driving me crazy: How to match all BinaryNodes so that Node flavor becomes "a parameter" so as to have generic swap that can be applied to any BinaryNode flavor to return swapped node of that flavor?

I know how to match all Trees that are BinaryNodes by using an active pattern:

let (|BinaryNode|_|) (tree : Tree) =
    match tree with
    | BinaryNodeA _ | BinaryNodeB _ -> Some(tree)
    | _ -> None

But that's not good enough because the following does not seem achievable:

match tree with
| [cases related to unary nodes..]
| BinaryNode a b -> BinaryNode b a

In other words, I have not found way to use BinaryNode flavor as if it were parameter like a and b. Instead, it seems I have to match each BinaryNode flavor separately. This could have practical significance if there were large number of binary node flavors. Type Tree is AST for Fsyacc/Fslex-generated parser/lexer, which limits options to restructure it. Any ideas?


Solution

  • You just need to change the definition of your active pattern:

    type Flavor = A | B
    
    let (|BinaryNode|_|) (tree : Tree) =
        match tree with
        | BinaryNodeA(x,y) -> Some(A,x,y) 
        | BinaryNodeB(x,y) -> Some(B,x,y)
        | _ -> None
    
    let mkBinaryNode f t1 t2 =
        match f with
        | A -> BinaryNodeA(t1,t2)
        | B -> BinaryNodeB(t1,t2)
    

    Then you can achieve what you want like this:

    match tree with
    | [cases related to unary nodes..]
    | BinaryNode(f,t1,t2) -> mkBinaryNode f t2 t1
    

    But if this is a common need then it might make sense to alter the definition of Tree to include flavor instead of dealing with it using active patterns.