我想在F#中建模一个层次结构,其中每个节点必须有一个父节点,显然是根节点,它没有父节点.我天真的解决方案
type Node = {
Id: int // meta data for node
Parent: Node option
}
let root = { Id = 1; Parent = None}
let child1 = { Id = 2; Parent = Some(root)}
let child2 = { Id = 3; Parent = Some(child1)}
Run Code Online (Sandbox Code Playgroud)
但是我进入F#已经通过@swlaschin而且他在创建描述性域名时让我大吃一惊.因此Parent
,当我有99%的时间需要时,这对我来说是一种选择.我尽力而为:
type Node =
| Node of NodeMeta * Node
| Root of NodeMeta
and NodeMeta = {
Id: int
}
let root = Root({Id = 1})
let child1 = Node({Id = 2}, root)
let child2 = Node({Id = 3}, child1)
Run Code Online (Sandbox Code Playgroud)
有更惯用的方式吗?
如果我在域驱动设计中为我自己的模型构建这个,我可能会定义节点如下:
[<Struct>] type NodeId = private NodeId of int
module NodeId =
let create id =
// Replace with the proper validation rules for a Node Id
if id < 0
then Error "NodeId must be non-negative" // I would actually use a DU with each error case
else Ok <| NodeId id
let value (NodeId id) = id
type [<Struct>] RootNode = {Id: NodeId}
type [<Struct>] ChildNode = {Parent: Node; Id: NodeId}
and Node =
| Root of RootNode
| Node of ChildNode
member node.Id =
match node with
| Root r -> r.Id
| Node n -> n.Id
member node.Parent =
match node with
| Root _ -> None
| Node n -> Some n.Parent
member node.IsRootNode =
match node with
| Root _ -> true
| Node _ -> false
member node.IsChildNode =
not node.IsRootNode
Run Code Online (Sandbox Code Playgroud)
这给了我们以下内容:
NodeId
该包装的类型int
和附带的模块,该模块封装了有效标识符的所有业务规则RootNode
和ChildNode
允许它们仅具有该类型节点的必需字段Node
允许我们表示RootNode
和ChildNode
作为相同类型而不要求它们具有相同字段但仍提供对底层字段的直接访问并允许我们轻松区分RootNode
s和ChildNode
s的单一类型然后,我会有一个用于创建Node
s 的模块:
module Node =
let createRoot =
NodeId.create >> Result.bind((fun id -> Root {Id = id}) >> Ok)
let createChild parent =
NodeId.create >> Result.bind((fun id -> Node {Id = id; Parent = parent}) >> Ok)
Run Code Online (Sandbox Code Playgroud)