3

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?

CaringDev
  • 8,391
  • 1
  • 24
  • 43
Serendip
  • 63
  • 1
  • 3
  • 5
    Why do you need multiple constructors for `BinaryNode`s? Could you change it to a single constructor with an additional `Tag` member which encodes the information e.g. `BinaryNode of Tag * Tree * Tree` and `type Tag = A | B`? – Lee Nov 28 '16 at 15:34

1 Answers1

5

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.

kvb
  • 54,864
  • 2
  • 91
  • 133
  • Based on the response, F# does not enable the exact kind of approach I was looking for. So I'm going to alter the tree by defining Flavor type and having one BinaryNode union member with Flavor definition. That's perfectly sufficient for this application, but I still feel that F# language could be more flexible here. – Serendip Nov 28 '16 at 17:46