Modeling complex hierarchies in F #

I am new to F # and I want to model things in the real world that have a rather complicated "have" relationship. At the top of the hierarchy are four types: A - D, with these relationships:

A | +--A | +--B | | | +--B | | | +--D | | | +--D | +--C | | : +--D | | | +--D : 

Thus, type B can have a β€œparent” of either A or B, and type D can have a parent of B, C, or D.

I want to use discriminatory unions to restrict the parent of each type, so they cannot be assigned to invalid parents, for example:

 type B_Parent = A | B type D_Parent = B | C | D 

Then I want to use records to model each type, where one field is a parent, for example:

 type A = { parent:A_Parent; ... } type B = { parent:B_Parent; ... } type C = { parent:C_Parent; ... } type D = { parent:D_Parent; ... } 

C_Parent is not a problem because its parent type A is declared in advance. And I used the "A" parameter for A_Parent. But I was not able to determine how to define B_Parent and D_Parent because of their nested dependency on themselves and other types?

+5
source share
2 answers

Firstly, one very important thing: when you write

 type B_Parent = A | B 

you are not declaring that B_Parent is a DU connecting the two previously defined types A and B There is no syntax for this.

What the line actually does is define two empty B_Parent subtypes that have nothing to do with the original A and B This is equivalent to:

 type B_Parent = B_Parent.A | B_Parent.B 

To reuse existing types inside a DU, you need to specify a name for the case β€” effectively wrap them with another type. So the correct declaration might be something like this:

 type B_Parent = P_a of A | P_b of B 

With this in mind, as Anton said, the and keyword is the key to defining reciprocal reference types. However, it is best to keep such reciprocal links as small and dense as possible. Therefore, we could do something like this:

 type A = { parent:A_Parent; ... } and A_Parent = P_a of A | None type B = { parent:B_Parent; ... } and B_Parent = P_a of A | P_b of B type C = { parent:C_Parent; ... } and C_Parent = P_a of A type D = { parent:D_Parent; ... } and D_Parent = P_b of B | P_c of C | P_d of D 

We could use only the A option for A_Parent and just A for C_Parent , but I think that keeping matches in file names will probably make reading more understandable.

+4
source

You can use the and keyword to define types that are related to each other, for example:

 type B_Parent = A | B and D_Parent = B | C | D and A_Parent = A and C_Parent = B and A = { parent:A_Parent } and B = { parent:B_Parent } and C = { parent:C_Parent } and D = { parent:D_Parent } 

See also this related SO question . The and keyword is also useful in defining mutually recursive functions .

+2
source

All Articles