如何在 Dhall 中将动态命名的记录与静态记录合并?
How to merge a dynamically named record with a static one in Dhall?
我正在 Dhall 中创建 AWS Step Function 定义。但是,我不知道如何创建它们用于 Choice
状态的通用结构,例如下面的示例:
{
"Not": {
"Variable": "$.type",
"StringEquals": "Private"
},
"Next": "Public"
}
Not
使用 mapKey
和 mapValue
非常简单。如果我定义一个基本的比较:
{ Type =
{ Variable : Text
, StringEquals : Optional Text
}
, default =
{ Variable = "foo"
, StringEquals = None Text
}
}
以及类型:
let ComparisonType = < And | Or | Not >
并添加辅助函数以将 mapKey
的类型呈现为 Text
:
let renderComparisonType = \(comparisonType : ComparisonType )
-> merge
{ And = "And"
, Or = "Or"
, Not = "Not"
}
comparisonType
然后我可以在函数中使用它们来中途生成记录:
let renderRuleComparisons =
\( comparisonType : ComparisonType ) ->
\( comparisons : List ComparisonOperator.Type ) ->
let keyName = renderComparisonType comparisonType
let compare = [ { mapKey = keyName, mapValue = comparisons } ]
in compare
如果我 运行 使用:
let rando = ComparisonOperator::{ Variable = "$.name", StringEquals = Some "Cow" }
let comparisons = renderRuleComparisons ComparisonType.Not [ rando ]
in comparisons
使用dhall-to-json
,她将输出第一部分:
{
"Not": {
"Variable": "$.name",
"StringEquals": "Cow"
}
}
...但我一直在努力将其与 "Next": "Sup"
合并。我已经使用了所有记录合并,如 /\
、//
等,但它一直给我各种我还不真正理解的类型错误。
首先,我将包括一种不进行类型检查的方法作为激发解决方案的起点:
let rando = ComparisonOperator::{ Variable = "$.name", StringEquals = Some "Cow" }
let comparisons = renderRuleComparisons ComparisonType.Not [ rando ]
in comparisons # toMap { Next = "Public" }
toMap
是将记录转换成键值列表的关键字,#
是列表拼接运算符。 Dhall CheatSheet 提供了一些如何使用它们的示例。
上述解决方案不起作用,因为#
无法合并具有不同元素类型的列表。 #
运算符的左侧具有以下类型:
comparisons : List { mapKey : Text, mapValue : Comparison.Type }
... 而 #
运算符的右侧具有此类型:
toMap { Next = "Public" } : List { mapKey : Text, mapValue : Text }
... 所以两个 List
不能按原样合并,因为 mapValue
字段的类型不同。
有两种方法可以解决这个问题:
- 方法 1:只要存在类型冲突就使用联合
- 方法 2:使用可以容纳任意值的弱类型 JSON 表示
方法 1 是这个特定示例的更简单的解决方案,方法 2 是更通用的解决方案,可以处理非常奇怪的 JSON 模式。
对于方法 1,dhall-to-json
将在转换为 JSON 时自动剥离非空联合构造函数(留下它们包装的值)。这意味着您可以转换 #
运算符的两个参数以就此通用类型达成一致:
List { mapKey : Text, mapValue : < State : Text | Comparison : Comparison.Type > }
...然后您应该能够连接两个键值对列表,dhall-to-json
将正确呈现它们。
还有第二种处理弱类型 JSON 模式的解决方案,您可以在此处了解更多信息:
基本思想是所有 JSON/YAML 集成都识别并支持弱类型 JSON 表示,可以容纳任意 JSON 数据,包括具有不同形状键的字典(就像你的例子)。您甚至不需要将整个表达式转换为这种弱类型表示;您只需要将此表示用于您 运行 遇到架构问题的配置子集。
这对您的示例意味着,您将更改 #
运算符的两个参数以具有此类型:
let Prelude = https://prelude.dhall-lang.org/v12.0.0/package.dhall
in List { mapKey : Text, mapValue : Prelude.JSON.Type }
documentation for Prelude.JSON.Type
也有更多关于如何使用此类型的详细信息。
我正在 Dhall 中创建 AWS Step Function 定义。但是,我不知道如何创建它们用于 Choice
状态的通用结构,例如下面的示例:
{
"Not": {
"Variable": "$.type",
"StringEquals": "Private"
},
"Next": "Public"
}
Not
使用 mapKey
和 mapValue
非常简单。如果我定义一个基本的比较:
{ Type =
{ Variable : Text
, StringEquals : Optional Text
}
, default =
{ Variable = "foo"
, StringEquals = None Text
}
}
以及类型:
let ComparisonType = < And | Or | Not >
并添加辅助函数以将 mapKey
的类型呈现为 Text
:
let renderComparisonType = \(comparisonType : ComparisonType )
-> merge
{ And = "And"
, Or = "Or"
, Not = "Not"
}
comparisonType
然后我可以在函数中使用它们来中途生成记录:
let renderRuleComparisons =
\( comparisonType : ComparisonType ) ->
\( comparisons : List ComparisonOperator.Type ) ->
let keyName = renderComparisonType comparisonType
let compare = [ { mapKey = keyName, mapValue = comparisons } ]
in compare
如果我 运行 使用:
let rando = ComparisonOperator::{ Variable = "$.name", StringEquals = Some "Cow" }
let comparisons = renderRuleComparisons ComparisonType.Not [ rando ]
in comparisons
使用dhall-to-json
,她将输出第一部分:
{
"Not": {
"Variable": "$.name",
"StringEquals": "Cow"
}
}
...但我一直在努力将其与 "Next": "Sup"
合并。我已经使用了所有记录合并,如 /\
、//
等,但它一直给我各种我还不真正理解的类型错误。
首先,我将包括一种不进行类型检查的方法作为激发解决方案的起点:
let rando = ComparisonOperator::{ Variable = "$.name", StringEquals = Some "Cow" }
let comparisons = renderRuleComparisons ComparisonType.Not [ rando ]
in comparisons # toMap { Next = "Public" }
toMap
是将记录转换成键值列表的关键字,#
是列表拼接运算符。 Dhall CheatSheet 提供了一些如何使用它们的示例。
上述解决方案不起作用,因为#
无法合并具有不同元素类型的列表。 #
运算符的左侧具有以下类型:
comparisons : List { mapKey : Text, mapValue : Comparison.Type }
... 而 #
运算符的右侧具有此类型:
toMap { Next = "Public" } : List { mapKey : Text, mapValue : Text }
... 所以两个 List
不能按原样合并,因为 mapValue
字段的类型不同。
有两种方法可以解决这个问题:
- 方法 1:只要存在类型冲突就使用联合
- 方法 2:使用可以容纳任意值的弱类型 JSON 表示
方法 1 是这个特定示例的更简单的解决方案,方法 2 是更通用的解决方案,可以处理非常奇怪的 JSON 模式。
对于方法 1,dhall-to-json
将在转换为 JSON 时自动剥离非空联合构造函数(留下它们包装的值)。这意味着您可以转换 #
运算符的两个参数以就此通用类型达成一致:
List { mapKey : Text, mapValue : < State : Text | Comparison : Comparison.Type > }
...然后您应该能够连接两个键值对列表,dhall-to-json
将正确呈现它们。
还有第二种处理弱类型 JSON 模式的解决方案,您可以在此处了解更多信息:
基本思想是所有 JSON/YAML 集成都识别并支持弱类型 JSON 表示,可以容纳任意 JSON 数据,包括具有不同形状键的字典(就像你的例子)。您甚至不需要将整个表达式转换为这种弱类型表示;您只需要将此表示用于您 运行 遇到架构问题的配置子集。
这对您的示例意味着,您将更改 #
运算符的两个参数以具有此类型:
let Prelude = https://prelude.dhall-lang.org/v12.0.0/package.dhall
in List { mapKey : Text, mapValue : Prelude.JSON.Type }
documentation for Prelude.JSON.Type
也有更多关于如何使用此类型的详细信息。