Skip to content
Permalink
Browse files

Prelude: Add standard representation for weakly-typed JSON values (#586)

This adds a new `JSON` type for representing unstructured JSON data
in Dhall.

The goal is to provide a way for users to have a way to pass through
unstructured data when integrating tools that have schema-free subsets
of their APIs.  An example of this is "pass-through" API fields which pass
through arbitrary JSON data without interpreting that data in any way.

For people familiar with Haskell, this newly-added JSON type is the Dhall
analog of `Data.Aeson.Value`.

This will be paired with a matching change to the `dhall-json` package
to recognize this type and support converting it back and forth to
`Data.Aeson.Value`s.
  • Loading branch information...
Gabriel439 committed Jun 20, 2019
1 parent e7eb533 commit 7a2acce9d445cd33920a91a8deee2d7ed3c16870
Showing with 489 additions and 4 deletions.
  1. +76 −0 Prelude/JSON/Type
  2. +43 −0 Prelude/JSON/array
  3. +38 −0 Prelude/JSON/bool
  4. +33 −0 Prelude/JSON/null
  5. +38 −0 Prelude/JSON/number
  6. +57 −0 Prelude/JSON/object
  7. +21 −0 Prelude/JSON/package.dhall
  8. +66 −0 Prelude/JSON/render
  9. +38 −0 Prelude/JSON/string
  10. +1 −1 Prelude/package.dhall
  11. +1 −1 release.nix
  12. +2 −2 scripts/lint-prelude.sh
  13. +10 −0 tests/normalization/success/prelude/JSON/Type/0A.dhall
  14. +11 −0 tests/normalization/success/prelude/JSON/Type/0B.dhall
  15. +2 −0 tests/normalization/success/prelude/JSON/array/0A.dhall
  16. +1 −0 tests/normalization/success/prelude/JSON/array/0B.dhall
  17. +3 −0 tests/normalization/success/prelude/JSON/array/1A.dhall
  18. +1 −0 tests/normalization/success/prelude/JSON/array/1B.dhall
  19. +2 −0 tests/normalization/success/prelude/JSON/bool/0A.dhall
  20. +1 −0 tests/normalization/success/prelude/JSON/bool/0B.dhall
  21. +2 −0 tests/normalization/success/prelude/JSON/bool/1A.dhall
  22. +1 −0 tests/normalization/success/prelude/JSON/bool/1B.dhall
  23. +2 −0 tests/normalization/success/prelude/JSON/null/0A.dhall
  24. +1 −0 tests/normalization/success/prelude/JSON/null/0B.dhall
  25. +2 −0 tests/normalization/success/prelude/JSON/number/0A.dhall
  26. +1 −0 tests/normalization/success/prelude/JSON/number/0B.dhall
  27. +2 −0 tests/normalization/success/prelude/JSON/number/1A.dhall
  28. +1 −0 tests/normalization/success/prelude/JSON/number/1B.dhall
  29. +7 −0 tests/normalization/success/prelude/JSON/object/0A.dhall
  30. +1 −0 tests/normalization/success/prelude/JSON/object/0B.dhall
  31. +4 −0 tests/normalization/success/prelude/JSON/object/1A.dhall
  32. +1 −0 tests/normalization/success/prelude/JSON/object/1B.dhall
  33. +12 −0 tests/normalization/success/prelude/JSON/render/0A.dhall
  34. +1 −0 tests/normalization/success/prelude/JSON/render/0B.dhall
  35. +2 −0 tests/normalization/success/prelude/JSON/string/0A.dhall
  36. +1 −0 tests/normalization/success/prelude/JSON/string/0B.dhall
  37. +2 −0 tests/normalization/success/prelude/JSON/string/1A.dhall
  38. +1 −0 tests/normalization/success/prelude/JSON/string/1B.dhall
@@ -0,0 +1,76 @@
{- Dhall encoding of an arbitrary JSON value
For example, the following JSON value:
```
[ { "foo": null, "bar": [ 1.0, true ] } ]
```
... corresponds to the following Dhall expression:
```
λ(JSON : Type)
→ λ ( json
: { array :
List JSON → JSON
, bool :
Bool → JSON
, null :
JSON
, number :
Double → JSON
, object :
List { mapKey : Text, mapValue : JSON } → JSON
, string :
Text → JSON
}
)
→ json.object
[ { mapKey = "foo", mapValue = json.null }
, { mapKey =
"bar"
, mapValue =
json.array [ json.number 1.0, json.bool True ]
}
]
```
You do not need to create these values directly, though. You can use
the utilities exported by `./package.dhall` to create values of this type,
such as:
```
let JSON = ./package.dhall
in JSON.object
[ { mapKey = "foo", mapValue = JSON.null }
, { mapKey =
"bar"
, mapValue =
JSON.array [ JSON.number 1.0, JSON.bool True ]
}
]
```
-}
let JSON/Type
: Type
= (JSON : Type)
( json
: { string :
Text JSON
, number :
Double JSON
, object :
List { mapKey : Text, mapValue : JSON } JSON
, array :
List JSON JSON
, bool :
Bool JSON
, null :
JSON
}
)
JSON

in JSON/Type
@@ -0,0 +1,43 @@
{- Create a JSON array from a `List` of JSON values
```
let JSON = ./package.dhall
in JSON.render (JSON.array [ JSON.number 1.0, JSON.bool True ])
= "[ 1.0, true ]"
let JSON/Type = ./Type
let JSON = ./package.dhall
in JSON.render (JSON.array ([] : List JSON/Type))
= "[ ]"
```
-}
let JSON =
./Type sha256:5adb234f5868a5b0eddeb034d690aaba8cb94ea20d0d557003e90334fff6be3e
? ./Type

let List/map =
../List/map sha256:dd845ffb4568d40327f2a817eb42d1c6138b929ca758d50bc33112ef3c885680
? ../List/map

let array
: List JSON JSON
= λ(x : List JSON)
λ(JSON : Type)
λ ( json
: { string :
Text JSON
, number :
Double JSON
, object :
List { mapKey : Text, mapValue : JSON } JSON
, array :
List JSON JSON
, bool :
Bool JSON
, null :
JSON
}
)
json.array (List/map JSON@1 JSON (λ(j : JSON@1) j JSON json) x)

in array
@@ -0,0 +1,38 @@
{- Create a JSON bool from a Dhall `Bool`
```
let JSON = ./package.dhall
in JSON.render (JSON.bool True)
= "true"
let JSON = ./package.dhall
in JSON.render (JSON.bool False)
= "false"
```
-}
let JSON =
./Type sha256:5adb234f5868a5b0eddeb034d690aaba8cb94ea20d0d557003e90334fff6be3e
? ./Type

let bool
: Bool JSON
= λ(x : Bool)
λ(JSON : Type)
λ ( json
: { string :
Text JSON
, number :
Double JSON
, object :
List { mapKey : Text, mapValue : JSON } JSON
, array :
List JSON JSON
, bool :
Bool JSON
, null :
JSON
}
)
json.bool x

in bool
@@ -0,0 +1,33 @@
{- Create a JSON null
```
let JSON = ./package.dhall
in JSON.render JSON.null
= "null"
```
-}
let JSON =
./Type sha256:5adb234f5868a5b0eddeb034d690aaba8cb94ea20d0d557003e90334fff6be3e
? ./Type

let null
: JSON
= λ(JSON : Type)
λ ( json
: { string :
Text JSON
, number :
Double JSON
, object :
List { mapKey : Text, mapValue : JSON } JSON
, array :
List JSON JSON
, bool :
Bool JSON
, null :
JSON
}
)
json.null

in null
@@ -0,0 +1,38 @@
{- Create a JSON number from a Dhall `Double`
```
let JSON = ./package.dhall
in JSON.render (JSON.number 42.0)
= "42.0"
let JSON = ./package.dhall
in JSON.render (JSON.number -1.5e-10)
= "-1.5e-10"
```
-}
let JSON =
./Type sha256:5adb234f5868a5b0eddeb034d690aaba8cb94ea20d0d557003e90334fff6be3e
? ./Type

let number
: Double JSON
= λ(x : Double)
λ(JSON : Type)
λ ( json
: { string :
Text JSON
, number :
Double JSON
, object :
List { mapKey : Text, mapValue : JSON } JSON
, array :
List JSON JSON
, bool :
Bool JSON
, null :
JSON
}
)
json.number x

in number
@@ -0,0 +1,57 @@
{- Create a JSON object from a Dhall `Map`
```
let JSON = ./package.dhall
in JSON.render
( JSON.object
[ { mapKey = "foo", mapValue = JSON.number 1.0 }
, { mapKey = "bar", mapValue = JSON.bool True }
]
)
= "{ \"foo\": 1.0, \"bar\": true }"
let JSON/Type = ./Type
let JSON = ./package.dhall
in JSON.render
(JSON.object ([] : List { mapKey : Text, mapValue : JSON/Type }))
= "{ }"
```
-}
let JSON =
./Type sha256:5adb234f5868a5b0eddeb034d690aaba8cb94ea20d0d557003e90334fff6be3e
? ./Type

let List/map =
../List/map sha256:dd845ffb4568d40327f2a817eb42d1c6138b929ca758d50bc33112ef3c885680
? ../List/map

let object
: List { mapKey : Text, mapValue : JSON } JSON
= λ(x : List { mapKey : Text, mapValue : JSON })
λ(JSON : Type)
λ ( json
: { string :
Text JSON
, number :
Double JSON
, object :
List { mapKey : Text, mapValue : JSON } JSON
, array :
List JSON JSON
, bool :
Bool JSON
, null :
JSON
}
)
json.object
( List/map
{ mapKey : Text, mapValue : JSON@1 }
{ mapKey : Text, mapValue : JSON }
( λ(kv : { mapKey : Text, mapValue : JSON@1 })
{ mapKey = kv.mapKey, mapValue = kv.mapValue JSON json }
)
x
)

in object
@@ -4,4 +4,25 @@
, keyValue =
./keyValue sha256:a0a97199d280c4cce72ffcbbf93b7ceda0a569cf4d173ac98e0aaaa78034b98c
? ./keyValue
, string =
./string sha256:7a8ac435d30a96092d72889f3d48eabf7cba47ecf553fd6bc07a79fdf473e8d2
? ./string
, number =
./number sha256:534745568065ae19d2b0fe1d09eeb071e9717d0f392187eb0bc95f386b018bec
? ./number
, object =
./object sha256:a4e047cf157c3971b026b3942a87d474c85950d9b9654f8ebc8631740abf75a9
? ./object
, array =
./array sha256:3a4c06cf135f4c80619e48c0808f6600d19782705bc59ee7c27cfc2e0f097eb7
? ./array
, bool =
./bool sha256:018d29f030b45d642aba6bb81bf2c19a7bf183684612ce7a2c8afd2099783c48
? ./bool
, null =
./null sha256:52c1d45ab2ca54875b444bfb1afdea497c8c9b0652e5044fafd8b16d97f4b78d
? ./null
, render =
./render sha256:81f5a84efbb35211b1556838e86d17ed497912cf765bfa4ab76708b21e5371f1
? ./render
}
@@ -0,0 +1,66 @@
{- Render a `JSON` value as `Text`
This is useful for debugging `JSON` values or for tests. For anything
more sophisticated you should use `dhall-to-json` or `dhall-to-yaml`
```
let JSON = ./package.dhall
in JSON.render
( JSON.array
[ JSON.bool True
, JSON.string "Hello"
, JSON.object
[ { mapKey = "foo", mapValue = JSON.null }
, { mapKey = "bar", mapValue = JSON.number 1.0 }
]
]
)
= "[ true, \"Hello\", { \"foo\": null, \"bar\": 1.0 } ]"
```
-}
let JSON =
./Type sha256:5adb234f5868a5b0eddeb034d690aaba8cb94ea20d0d557003e90334fff6be3e
? ./Type

let Text/concatMapSep =
../Text/concatMapSep sha256:c272aca80a607bc5963d1fcb38819e7e0d3e72ac4d02b1183b1afb6a91340840
? ../Text/concatMapSep

let Text/concatMap =
../Text/concatMap sha256:7a0b0b99643de69d6f94ba49441cd0fa0507cbdfa8ace0295f16097af37e226f
? ../Text/concatMap

let render
: JSON Text
= λ(j : JSON)
j
Text
{ string =
λ(x : Text) Text/show x
, number =
λ(x : Double) Double/show x
, object =
λ(x : List { mapKey : Text, mapValue : Text })
let body =
Text/concatMapSep
","
{ mapKey : Text, mapValue : Text }
( λ(e : { mapKey : Text, mapValue : Text })
" ${Text/show e.mapKey}: ${e.mapValue}"
)
x

in "{${body} }"
, array =
λ(x : List Text)
let body = Text/concatMapSep "," Text (λ(y : Text) " ${y}") x

in "[${body} ]"
, bool =
λ(x : Bool) if x then "true" else "false"
, null =
"null"
}

in render

0 comments on commit 7a2acce

Please sign in to comment.
You can’t perform that action at this time.