Skip to content

Commit fea201b

Browse files
committed
Docs
1 parent aa21be9 commit fea201b

File tree

2 files changed

+179
-1
lines changed

2 files changed

+179
-1
lines changed

README.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,142 @@ bower install purescript-argonaut-core
1616
## Documentation
1717

1818
Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-argonaut-core).
19+
20+
## Tutorial
21+
22+
Some of Argonaut's functions might seem a bit arcane at first, so it can help
23+
to understand the underlying design decisions which make it the way it is.
24+
25+
One approach for modelling JSON values would be to define an ADT, like this:
26+
27+
```purescript
28+
data Json
29+
= JNull
30+
| JBoolean Boolean
31+
| JArray (Array Json)
32+
| JObject (StrMap Json)
33+
[...]
34+
```
35+
36+
And indeed, some might even say this is the obvious approach.
37+
38+
Because Argonaut is written with the compilation target of JavaScript in mind,
39+
it takes a slightly different approach, which is to reuse the existing data
40+
types which JavaScript already provides. This way, the result of JavaScript's
41+
`JSON.stringify` function is already a `Json` value, and no extra processing is
42+
needed before you can start operating on it. This ought to help your program
43+
both in terms of speed and memory churn.
44+
45+
Much of the design of Argonaut follows naturally from this design decision.
46+
47+
### Introducing Json values
48+
49+
(Or, where do `Json` values come from?)
50+
51+
Usually, a `Json` value will be introduced into your program via either the FFI
52+
or via the construction functions in `Data.Argonaut.Core`. Here are some
53+
examples:
54+
55+
```javascript
56+
// In an FFI module.
57+
exports.someNumber = 23.6;
58+
exports.someBoolean = false;
59+
exports.someObject = {people: [{name: "john"}, {name: "jane"}],
60+
```
61+
62+
```purescript
63+
foreign import someNumber :: Json
64+
foreign import someBoolean :: Json
65+
foreign import someObject :: Json
66+
```
67+
68+
Generally, if a JavaScript value could be returned from a call to
69+
`JSON.stringify`, it's fine to import it from the FFI as `Json`. So, for
70+
example, objects, booleans, numbers, strings, and arrays are all fine, but
71+
functions are not.
72+
73+
The construction functions (that is, `fromX`, or `jsonX`) can be used as
74+
follows:
75+
76+
```purescript
77+
import Data.Tuple (Tuple(..))
78+
import Data.StrMap as StrMap
79+
import Data.Argonaut.Core as A
80+
81+
someNumber = A.fromNumber 23.6
82+
someBoolean = A.fromBoolean false
83+
someObject = A.fromObject (StrMap.fromFoldable [
84+
Tuple "people" (A.fromArray [
85+
A.jsonSingletonObject "name" (A.fromString "john"),
86+
A.jsonSingletonObject "name" (A.fromString "jane")
87+
])
88+
])
89+
```
90+
91+
### Eliminating/matching on `Json` values
92+
93+
We can perform case analysis for `Json` values using the `foldJson` function.
94+
This function is necessary because `Json` is not an algebraic data type. If
95+
`Json` were an algebraic data type, we would not have as much need for this
96+
function, because we could perform pattern matching with a `case ... of`
97+
expression instead.
98+
99+
For example, imagine we had the following values defined in JavaScript and
100+
imported via the FFI:
101+
102+
```javascript
103+
exports.someNumber = 0.0;
104+
exports.someArray = [0.0, {foo: 'bar'}, false];
105+
exports.someObject = {foo: 1, bar: [2,2]};
106+
```
107+
108+
Then we can match on them in PureScript using `foldJson`:
109+
110+
```purescript
111+
foreign import someNumber :: Json
112+
foreign import someArray :: Json
113+
foreign import someObject :: Json
114+
115+
basicInfo :: Json -> String
116+
basicInfo = foldJson
117+
(const "It was null")
118+
(\b -> "Got a boolean: " <>
119+
if b then "it was true!" else "It was false.")
120+
(\x -> "Got a number: " <> show x)
121+
(\s -> "Got a string, which was " <> Data.String.length s <>
122+
" characters long.")
123+
(\xs -> "Got an array, which had " <> Data.Array.length xs <>
124+
" items.")
125+
(\obj -> "Got an object, which had " <> Data.StrMap.size obj <>
126+
" items.")
127+
```
128+
129+
```purescript
130+
basicInfo someNumber -- => "Got a number: 0.0"
131+
basicInfo someArray -- => "Got an array, which had 3 items."
132+
basicInfo someObject -- => "Got an object, which had 2 items."
133+
```
134+
135+
All the other functions for matching on `Json` values can be expressed in terms
136+
of `foldJson`, but a few others are provided for convenience. For example, the
137+
`foldJsonX` functions can be used to match on a specific type. The first
138+
argument acts as a default value, to be used if the `Json` value turned out not
139+
to be that type. For example, we can write a function which tests whether a
140+
JSON value is the string "lol" like this:
141+
142+
```purescript
143+
isJsonLol = foldJsonString false (_ == "lol")
144+
```
145+
146+
If the `Json` value is not a string, the default `false` is used. Otherwise,
147+
we test whether the string is equal to "lol".
148+
149+
The `toX` functions also occupy a similar role. We could have written
150+
`isJsonLol` like this, too:
151+
152+
```purescript
153+
isJsonLol json =
154+
case toString json of
155+
Just str -> str == "lol"
156+
Nothing -> false
157+
```

src/Data/Argonaut/Core.purs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
-- | This module defines a data type and various functions for creating and
2+
-- | manipulating JSON values. The README contains additional documentation
3+
-- | for this module.
14
module Data.Argonaut.Core
25
( Json(..)
36
, JNull(..)
@@ -50,37 +53,73 @@ import Data.Function
5053

5154
import qualified Data.StrMap as M
5255

56+
-- | A Boolean value inside some JSON data. Note that this type is exactly the
57+
-- | same as the primitive `Boolean` type; this synonym acts only to help
58+
-- | indicate intent.
5359
type JBoolean = Boolean
60+
61+
-- | A Number value inside some JSON data. Note that this type is exactly the
62+
-- | same as the primitive `Number` type; this synonym acts only to help
63+
-- | indicate intent.
5464
type JNumber = Number
65+
66+
-- | A String value inside some JSON data. Note that this type is exactly the
67+
-- | same as the primitive `String` type; this synonym acts only to help
68+
-- | indicate intent.
5569
type JString = String
56-
type JAssoc = Tuple String Json
70+
71+
-- | A JSON array; an array containing `Json` values.
5772
type JArray = Array Json
73+
74+
-- | A JSON object; a JavaScript object containing `Json` values.
5875
type JObject = M.StrMap Json
5976

77+
type JAssoc = Tuple String Json
78+
79+
-- | The type of null values inside JSON data. There is exactly one value of
80+
-- | this type: in JavaScript, it is written `null`. This module exports this
81+
-- | value as `jsonNull`.
6082
foreign import data JNull :: *
83+
84+
-- | The type of JSON data. The underlying representation is the same as what
85+
-- | would be returned from JavaScript's `JSON.stringify` function; that is,
86+
-- | ordinary JavaScript booleans, strings, arrays, objects, etc.
6187
foreign import data Json :: *
6288

89+
-- | Case analysis for `Json` values. See the README for more information.
6390
foldJson :: forall a.
6491
(JNull -> a) -> (JBoolean -> a) -> (JNumber -> a) ->
6592
(JString -> a) -> (JArray -> a) -> (JObject -> a) ->
6693
Json -> a
6794
foldJson a b c d e f json = runFn7 _foldJson a b c d e f json
6895

96+
-- | A simpler version of `foldJson` which accepts a callback for when the
97+
-- | `Json` argument was null, and a default value for all other cases.
6998
foldJsonNull :: forall a. a -> (JNull -> a) -> Json -> a
7099
foldJsonNull d f j = runFn7 _foldJson f (const d) (const d) (const d) (const d) (const d) j
71100

101+
-- | A simpler version of `foldJson` which accepts a callback for when the
102+
-- | `Json` argument was a `Boolean`, and a default value for all other cases.
72103
foldJsonBoolean :: forall a. a -> (JBoolean -> a) -> Json -> a
73104
foldJsonBoolean d f j = runFn7 _foldJson (const d) f (const d) (const d) (const d) (const d) j
74105

106+
-- | A simpler version of `foldJson` which accepts a callback for when the
107+
-- | `Json` argument was a `Number`, and a default value for all other cases.
75108
foldJsonNumber :: forall a. a -> (JNumber -> a) -> Json -> a
76109
foldJsonNumber d f j = runFn7 _foldJson (const d) (const d) f (const d) (const d) (const d) j
77110

111+
-- | A simpler version of `foldJson` which accepts a callback for when the
112+
-- | `Json` argument was a `String`, and a default value for all other cases.
78113
foldJsonString :: forall a. a -> (JString -> a) -> Json -> a
79114
foldJsonString d f j = runFn7 _foldJson (const d) (const d) (const d) f (const d) (const d) j
80115

116+
-- | A simpler version of `foldJson` which accepts a callback for when the
117+
-- | `Json` argument was a `JArray`, and a default value for all other cases.
81118
foldJsonArray :: forall a. a -> (JArray -> a) -> Json -> a
82119
foldJsonArray d f j = runFn7 _foldJson (const d) (const d) (const d) (const d) f (const d) j
83120

121+
-- | A simpler version of `foldJson` which accepts a callback for when the
122+
-- | `Json` argument was a `JObject`, and a default value for all other cases.
84123
foldJsonObject :: forall a. a -> (JObject -> a) -> Json -> a
85124
foldJsonObject d f j = runFn7 _foldJson (const d) (const d) (const d) (const d) (const d) f j
86125

0 commit comments

Comments
 (0)