使用 Dhall 将递归类型编码为 JSON

问题描述

我想要的输出的简化版本:

{
  "dynamic-name": {
    "type": "type","fields": {
      "inner-dynamic-name": {
        "type": "inner-type","analyzer": "analyzer"
      }
    }
  }
}

这是我为生成它而编写的 Dhall 代码

let Field
    : Type
    = ∀(Field : Type) →
      ∀ ( Leaf
        : { mapKey : Text,mapValue : { type : Text,analyzer : Optional Text } } →
            Field
        ) →
      ∀ ( Node
        : { mapKey : Text,fields : List Field } } →
            Field
        ) →
        Field

let example
    : Field
    = λ(Field : Type) →
      λ ( Leaf
        : { mapKey : Text,analyzer : Optional Text } } →
            Field
        ) →
      λ ( Node
        : { mapKey : Text,fields : List Field } } →
            Field
        ) →
        Node
          { mapKey = "dynamic-name",mapValue =
            { type = "type",fields =
              [ Leaf
                  { mapKey = "inner-dynamic-name",mapValue =
                    { type = "inner-type",analyzer = Some "analyzer" }
                  }
              ]
            }
          }

in  example

但是,当将我的 Dhall 配置传递给 dhall-to-json 时,我收到以下错误

Error: Cannot translate to JSON                                            
                                                                                
Explanation: Only primitive values,records,unions,❰List❱s,and ❰Optional❱    
values can be translated from Dhall to JSON                                     
                                                                                
The following Dhall expression Could not be translated to JSON:                 
                                                                                
↳ λ(_ : Type) →
  λ ( _
    : { mapKey : Text,mapValue : { analyzer : Optional Text,type : Text } } →
        _@1
    ) →
  λ(_ : { mapKey : Text,mapValue : { fields : List _@1,type : Text } } → _@2) →
    _
      { mapKey = "dynamic-name",mapValue =
        { fields =
          [ _@1
              { mapKey = "inner-dynamic-name",mapValue = { analyzer = Some "analyzer",type = "inner-type" }
              }
          ],type = "type"
        }
      }

我正在运行 dhall-to-json 的 1.7.6 版。我做错了什么?

(忽略:我需要包含更多单词才能发布我的问题,但任何更多似乎都是多余的。最后几句话是我是黑客。)

解决方法

我将我的回答从 https://github.com/dhall-lang/dhall-haskell/issues/2259 复制到这里:

dhall-to-json 不能直接处理自定义递归类型,但它可以处理一种特殊的递归类型,即 Prelude.JSON.Type,所以这样的事情会起作用:

let JSON =
      https://prelude.dhall-lang.org/JSON/package.dhall
        sha256:5f98b7722fd13509ef448b075e02b9ff98312ae7a406cf53ed25012dbc9990ac

let Field
    : Type
    = ∀(Field : Type) →
      ∀ ( Leaf
        : { mapKey : Text,mapValue : { type : Text,analyzer : Optional Text }
          } →
            Field
        ) →
      ∀ ( Node
        : { mapKey : Text,fields : List Field } } →
            Field
        ) →
        Field

let example
    : Field
    = λ(Field : Type) →
      λ ( Leaf
        : { mapKey : Text,analyzer : Optional Text }
          } →
            Field
        ) →
      λ ( Node
        : { mapKey : Text,fields : List Field } } →
            Field
        ) →
        Node
          { mapKey = "dynamic-name",mapValue =
            { type = "type",fields =
              [ Leaf
                  { mapKey = "inner-dynamic-name",mapValue =
                    { type = "inner-type",analyzer = Some "analyzer" }
                  }
              ]
            }
          }

let Field/toJSON
    : Field → JSON.Type
    = λ(field : Field) →
        field
          JSON.Type
          ( λ ( args
              : { mapKey : Text,analyzer : Optional Text }
                }
              ) →
              JSON.object
                [ { mapKey = args.mapKey,mapValue =
                      JSON.object
                        ( toMap
                            { type = JSON.string args.mapValue.type,analyzer =
                                merge
                                  { None = JSON.null,Some = JSON.string }
                                  args.mapValue.analyzer
                            }
                        )
                  }
                ]
          )
          ( λ ( args
              : { mapKey : Text,fields : List JSON.Type }
                }
              ) →
              JSON.object
                [ { mapKey = args.mapKey,fields = JSON.array args.mapValue.fields
                            }
                        )
                  }
                ]
          )

in  Field/toJSON example

... 并且 dhall-to-json 接受:

$ dhall-to-json --file ./example.dhall
{
  "dynamic-name": {
    "fields": [
      {
        "inner-dynamic-name": {
          "analyzer": "analyzer","type": "inner-type"
        }
      }
    ],"type": "type"
  }
}