f# - 创建 JSON 树时模块之间的循环依赖
问题描述
我开发了一个库,它使用反射为给定的类创建 JSON Hyperschema。我选择了 F# 作为编程语言,但没有太多经验。
像其他人(this or this question)一样,我面临的问题是我想在模块之间创建循环依赖,但我不能......
应该给库的 main 函数 aType
并返回 a JsonSchema
。
type JsonSchema = {
Type: JsonType
Links: seq<LinkDescriptor>
}
AJsonType
是基元、数组、对象或可为空的。但是数组有一个items
类型的字段JsonSchema
,一个对象有一个列表JsonProperties
,它再次引用JsonSchema
。
type JsonType =
| JsonBoolean
| JsonInteger
| JsonNumber
| JsonString
| JsonArray of items: JsonSchema
| JsonObject of properties: seq<JsonProperty>
| Nullable of underlyingType: JsonType
type JsonProperty = {
Schema: JsonSchema
Name: string
}
然后是 中LinkDescriptor
使用的类型JsonSchema
,它再次具有类型字段JsonSchema
。
type LinkDescriptor = {
Rel: string
Href: UriTemplate
HrefSchema: JsonSchema option
}
如果我采用这种方法并将所有类型写入一个文件并将它们与 链接起来and
,我将被迫使创建类型的函数相互递归。
let rec getJsonType (t: Type): JsonType =
// Handle primitive types
if (t.IsArray) then
let itemsType = t.GetElementType()
let schema = {
Type = getJsonType itemsType
Links = getLinksForType itemsType
}
JsonArray schema
else JsonObject <| getJsonProperties t
and getJsonProperties (t: Type): seq<JsonProperty> =
getPublicProperties t
|> Seq.map (fun p -> {
Name = p.Name
Schema = {
Type = getJsonType p.PropertyType
Links = getLinksForProperty p
}
})
and getLinksForType (t: Type): seq<LinkDescriptor> =
getLinkAttributes t
|> Seq.map (fun attr -> {
Rel = attr.Rel
Href = attr.Href
HrefSchema =
if isNull attr.HrefSchemaType
then None
else Some { Type = getJsonType attr.HrefSchemaType; Links = Seq.empty<LinkDescriptor> }
})
实际上,这些功能有点复杂,这就是为什么我希望它们位于不同的模块中。
我已阅读Refactoring to remove cyclic dependencies,但不知道如何在我的案例中应用参数化。
我能想到的唯一想法是:我一次只创建一个层,而不是一次创建完整的 JSON 树。也就是说,JsonType
存储底层System.Type
:
type JsonType =
| JsonBoolean
// ...
| JsonArray of Type
| JsonObject of Type
| Nullable of underlyingType: JsonType
然后我可以创建一个JsonType
包含类似函数的模块getItemsSchema
,它JsonSchema
根据存储的JsonArray
. 也是如此getObjectProperties
。
但是我对这种方法并不是很满意,因为它感觉有点像一个泄漏的抽象。
您对我如何改进上述代码以摆脱循环依赖有什么建议吗?
解决方案
鉴于您的函数逻辑是紧密耦合的,我认为它们应该保留在同一个模块中。
但是,如果您仍然想分解它们,您可以执行以下操作:
想象一下,您有以下一组相互依赖的函数:
let rec f x = g x + h x
and g x = f x
and h x = f x
为了在自己的模块中声明每一个,您可以显式定义它们的函数依赖关系。例如函数 f 依赖于函数 g 和 h:
module moduleF =
let _f (g:int->int) (h:int->int) x = g x + h x
您可以对 g 和 h 执行相同的操作,同时在不同的模块中实现它们:
module moduleG =
let _g (f:int->int) x = f x
module moduleH =
let _h (f:int->int) x = f x
最后,您可以将它们全部集成到第四个模块中,为您的每个功能提供必要的依赖关系。
module Final =
open ModuleF
open ModuleG
open ModuleH
let rec f x = _f g h x
and g x = _g f x
and h x = _h f x
推荐阅读
- python - 熊猫数据框 loc 中的条件检查
- java - 在完全托管的 CloudRun 上使用 micronaut
- python - 将包含转义字符的字符串转换为字典
- r - 使用 R 中的 highcharter 为 highcharts 中的误差线图设置双轴标签
- javascript - 为热图中的每一行分配不同的颜色
- python-3.x - 运行时使用多处理崩溃的编译脚本
- python-3.x - 如何在使用 Python 和 swagger 开发的 IBM Cloud 上部署我的 API?
- node.js - Firebase 安装错误,“未找到 grpc@1.19.0 和 node@12.1.0 的预构建二进制文件”
- jolt - 新手:将 json 重复转换为单个数组并在里面添加条目
- c# - 将数据网格列绑定到视图模型