在F#中建模复杂的层次结构

Den*_*isV 5 f#

我是F#的新手,我想在现实世界中建模具有相当复杂的"有一个"关系的东西.在层次结构的顶部有四种类型,A - D,具有以下关系:

A
|
+--A
|
+--B
|  |
|  +--B
|  |
|  +--D
|     |
|     +--D
|
+--C
|  |
:  +--D
|     |
|     +--D
:
Run Code Online (Sandbox Code Playgroud)

因此,类型B可以具有A或B的"父",而类型D可以具有B,C或D的父级.

我想使用有区别的联合来约束每种类型的父级,因此不能将它们分配给无效的父级,例如:

type B_Parent = A | B
type D_Parent = B | C | D
Run Code Online (Sandbox Code Playgroud)

然后我想使用记录来模拟每个类型,其中一个字段是父类,例如:

type A = { parent:A_Parent; ... }
type B = { parent:B_Parent; ... }
type C = { parent:C_Parent; ... }
type D = { parent:D_Parent; ... }
Run Code Online (Sandbox Code Playgroud)

C_Parent不是问题,因为它的父类型A是事先声明的.我为A_Parent使用了'A选项'.但我还没有弄清楚如何定义B_Parent和D_Parent,因为它们对自己和其他类型的嵌套依赖?

pia*_*ste 5

首先,一件非常重要的事情:当你写的时候

type B_Parent = A | B
Run Code Online (Sandbox Code Playgroud)

并不是在声明这B_Parent是一个将两个先前定义的类型A和结合在一起的DU B。没有语法。

上面的代码行实际上是在定义两个空的子类型B_Parent,它们与原始Aand 无关B。它等效于:

type B_Parent = B_Parent.A | B_Parent.B
Run Code Online (Sandbox Code Playgroud)

为了重用DU中的现有类型,您需要为案例指定一个名称-有效地将它们包装在另一种类型中。因此正确的声明可以是:

type B_Parent = P_a of A | P_b of B
Run Code Online (Sandbox Code Playgroud)

除此以外-正如安东所说,用于定义相互参照类型的关键字是and。也就是说,最好使此类相互引用尽可能小而紧密。所以我们可以做这样的事情:

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
Run Code Online (Sandbox Code Playgroud)

我们可以用刚刚A optionA_Parent,只是AC_Parent,但我认为,保持案件名称一致将可能使事情变得更具有可读性。