@@ -22,41 +22,65 @@ Module documentation is [published on Pursuit](http://pursuit.purescript.org/pac
22
22
Some of Argonaut's functions might seem a bit arcane at first, so it can help
23
23
to understand the underlying design decisions which make it the way it is.
24
24
25
- One approach for modelling JSON values would be to define an ADT, like this:
25
+ One approach for modelling JSON values would be to define an algebraic data
26
+ type, like this:
26
27
27
28
``` purescript
28
29
data Json
29
30
= JNull
31
+ | JString String
32
+ | JNumber Number
30
33
| JBoolean Boolean
31
34
| JArray (Array Json)
32
35
| JObject (StrMap Json)
33
- [...]
34
36
```
35
37
36
38
And indeed, some might even say this is the obvious approach.
37
39
38
40
Because Argonaut is written with the compilation target of JavaScript in mind,
39
41
it takes a slightly different approach, which is to reuse the existing data
40
42
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
43
+ ` JSON.parse ` function is already a ` Json ` value, and no extra processing is
42
44
needed before you can start operating on it. This ought to help your program
43
45
both in terms of speed and memory churn.
44
46
45
47
Much of the design of Argonaut follows naturally from this design decision.
46
48
49
+ ### Types
50
+
51
+ The most important type in this library is, of course, ` Json ` , which is the
52
+ type of JSON data in its native JavaScript representation.
53
+
54
+ As the (hypothetical) algebraic data type declaration above indicates, there
55
+ are six possibilities for a JSON value: it can be ` null ` , a string, a number, a
56
+ boolean, an array of JSON values, or an object mapping string keys to JSON
57
+ values.
58
+
59
+ For convenience, and to ensure that values have the appropriate underlying
60
+ data representations, Argonaut also declares types for each of these individual
61
+ possibilities, whose names correspond to the data constructor names above.
62
+
63
+ Therefore, ` JString ` , ` JNumber ` , and ` JBoolean ` are synonyms for the primitive
64
+ PureScript types ` String ` , ` Number ` , and ` Boolean ` respectively; ` JArray ` is a
65
+ synonym for ` Array Json ` ; and ` JObject ` is a synonym for ` StrMap Json ` .
66
+ Argonaut defines a type ` JNull ` as the type of the ` null ` value in JavaScript.
67
+
47
68
### Introducing Json values
48
69
49
70
(Or, where do ` Json ` values come from?)
50
71
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:
72
+ If your program is receiving JSON data as a string, you probably want the
73
+ ` parseJson ` function in ` Data.Argonaut.Parser ` , which is a very simple wrapper
74
+ around JavaScript's ` JSON.parse ` .
75
+
76
+ Otherwise, ` Json ` values can be introduced into your program via the FFI or via
77
+ the construction functions in ` Data.Argonaut.Core ` . Here are some examples:
54
78
55
79
``` javascript
56
80
// In an FFI module.
57
81
exports .someNumber = 23.6 ;
58
82
exports .someBoolean = false ;
59
- exports .someObject = {people: [{name: " john" }, {name: " jane" }],
83
+ exports .someObject = {people: [{name: " john" }, {name: " jane" }], common_interests : []};
60
84
```
61
85
62
86
``` purescript
@@ -65,10 +89,9 @@ foreign import someBoolean :: Json
65
89
foreign import someObject :: Json
66
90
```
67
91
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.
92
+ Generally, if a JavaScript value could be returned from a call to ` JSON.parse ` ,
93
+ it's fine to import it from the FFI as ` Json ` . So, for example, objects,
94
+ booleans, numbers, strings, and arrays are all fine, but functions are not.
72
95
73
96
The construction functions (that is, ` fromX ` , or ` jsonX ` ) can be used as
74
97
follows:
@@ -84,7 +107,8 @@ someObject = A.fromObject (StrMap.fromFoldable [
84
107
Tuple "people" (A.fromArray [
85
108
A.jsonSingletonObject "name" (A.fromString "john"),
86
109
A.jsonSingletonObject "name" (A.fromString "jane")
87
- ])
110
+ ]),
111
+ Tuple "common_interests" A.jsonEmptyArray
88
112
])
89
113
```
90
114
@@ -96,21 +120,44 @@ This function is necessary because `Json` is not an algebraic data type. If
96
120
function, because we could perform pattern matching with a ` case ... of `
97
121
expression instead.
98
122
123
+ The type of ` foldJson ` is:
124
+
125
+ ``` purescript
126
+ foldJson :: forall a.
127
+ (JNull -> a) -> (JBoolean -> a) -> (JNumber -> a) ->
128
+ (JString -> a) -> (JArray -> a) -> (JObject -> a) ->
129
+ Json -> a
130
+ ```
131
+
132
+ That is, ` foldJson ` takes six functions, which all must return values of some
133
+ particular type ` a ` , together with one ` Json ` value. ` foldJson ` itself also
134
+ returns a value of the same type ` a ` .
135
+
136
+ A use of ` foldJson ` is very similar to a ` case ... of ` expression, as it allows
137
+ you to handle each of the six possibilities for the ` Json ` value you passed in.
138
+ Thinking of it this way, each of the six function arguments is like one of the
139
+ case alternatives. Just like in a ` case ... of ` expression, the final value
140
+ that the whole expression evaluates to comes from evaluating exactly one of the
141
+ 'alternatives' (functions) that you pass in. In fact, you can tell that this
142
+ is the case just by looking at the type signature of ` foldJson ` , because of a
143
+ property called * parametricity* (although a deeper explanation of parametricity
144
+ is outside the scope of this tutorial).
145
+
99
146
For example, imagine we had the following values defined in JavaScript and
100
147
imported via the FFI:
101
148
102
149
``` javascript
103
- exports .someNumber = 0.0 ;
104
- exports .someArray = [0.0 , {foo: ' bar' }, false ];
105
- exports .someObject = {foo: 1 , bar: [2 ,2 ]};
150
+ exports .anotherNumber = 0.0 ;
151
+ exports .anotherArray = [0.0 , {foo: ' bar' }, false ];
152
+ exports .anotherObject = {foo: 1 , bar: [2 ,2 ]};
106
153
```
107
154
108
155
Then we can match on them in PureScript using ` foldJson ` :
109
156
110
157
``` purescript
111
- foreign import someNumber :: Json
112
- foreign import someArray :: Json
113
- foreign import someObject :: Json
158
+ foreign import anotherNumber :: Json
159
+ foreign import anotherArray :: Json
160
+ foreign import anotherObject :: Json
114
161
115
162
basicInfo :: Json -> String
116
163
basicInfo = foldJson
@@ -127,29 +174,38 @@ basicInfo = foldJson
127
174
```
128
175
129
176
``` 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."
177
+ basicInfo anotherNumber -- => "Got a number: 0.0"
178
+ basicInfo anotherArray -- => "Got an array, which had 3 items."
179
+ basicInfo anotherObject -- => "Got an object, which had 2 items."
133
180
```
134
181
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:
182
+ ` foldJson ` is the fundamental function for pattern matching on ` Json ` values;
183
+ any kind of pattern matching you might want to do can be done with ` foldJson ` .
184
+
185
+ However, ` foldJson ` is not always comfortable to use, so Argonaut provides a
186
+ few other simpler versions for convenience. For example, the ` foldJsonX `
187
+ functions can be used to match on a specific type. The first argument acts as a
188
+ default value, to be used if the ` Json ` value turned out not to be that type.
189
+ For example, we can write a function which tests whether a JSON value is the
190
+ string "lol" like this:
141
191
142
192
``` purescript
193
+ foldJsonString :: forall a. a -> (JString -> a) -> Json -> a
194
+
143
195
isJsonLol = foldJsonString false (_ == "lol")
144
196
```
145
197
146
198
If the ` Json ` value is not a string, the default ` false ` is used. Otherwise,
147
199
we test whether the string is equal to "lol".
148
200
149
- The ` toX` functions also occupy a similar role. We could have written
150
- ` isJsonLol` like this, too:
201
+ The ` toX ` functions also occupy a similar role: they attempt to convert ` Json `
202
+ values into a specific type. If the json value you provide is of the right
203
+ type, you'll get a ` Just ` value. Otherwise, you'll get ` Nothing ` . For example,
204
+ we could have written ` isJsonLol ` like this, too:
151
205
152
206
``` purescript
207
+ toString :: Json -> Maybe JString
208
+
153
209
isJsonLol json =
154
210
case toString json of
155
211
Just str -> str == "lol"
0 commit comments