Как объединить запись с динамическим именем в stah c в Dhall? - PullRequest
0 голосов
/ 10 февраля 2020

Я создаю определение AWS Step Function в Dhall. Однако я не знаю, как создать общую структуру, которую они используют для Choice состояний, как в примере ниже:

{
    "Not": {
        "Variable": "$.type",
        "StringEquals": "Private"
    },
    "Next": "Public"
}

Not довольно просто, используя mapKey и mapValue , Если я определю базовое c Сравнение:

{ Type =
    { Variable : Text
    , StringEquals : Optional Text
    }
, default = 
    { Variable = "foo" 
    , StringEquals = None Text
    }
}

И типы:

let ComparisonType = < And | Or | Not >

И добавление вспомогательной функции для отображения типа как Text для mapKey :

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". Я использовал все слияния записей, такие как /\, //, et c. и это продолжает давать мне различные ошибки типа, которые я до сих пор не понимаю.

1 Ответ

1 голос
/ 10 февраля 2020

Сначала я включу подход, который не проверяет тип в качестве отправной точки, чтобы мотивировать решение:

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 схемами, о котором вы можете узнать подробнее здесь:

Основная идея c заключается в том, что все интеграции JSON / YAML распознают и поддерживают слабо типизированное представление JSON он может содержать произвольные данные JSON, включая словари с ключами различной формы (как в вашем примере). Вам даже не нужно преобразовывать все выражение в это слабо типизированное представление; вам нужно использовать это представление только для подмножества вашей конфигурации, где вы сталкиваетесь с проблемами схемы.

Для вашего примера это означает, что вы должны изменить оба аргумента на оператор #, чтобы иметь этот тип :

let Prelude = https://prelude.dhall-lang.org/v12.0.0/package.dhall

in  List { mapKey : Text, mapValue : Prelude.JSON.Type }

Документация для Prelude.JSON.Type также содержит более подробную информацию о том, как использовать этот тип.

...