@@ -22,41 +22,65 @@ Module documentation is [published on Pursuit](http://pursuit.purescript.org/pac
2222Some of Argonaut's functions might seem a bit arcane at first, so it can help
2323to understand the underlying design decisions which make it the way it is.
2424
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:
2627
2728``` purescript
2829data Json
2930 = JNull
31+ | JString String
32+ | JNumber Number
3033 | JBoolean Boolean
3134 | JArray (Array Json)
3235 | JObject (StrMap Json)
33- [...]
3436```
3537
3638And indeed, some might even say this is the obvious approach.
3739
3840Because Argonaut is written with the compilation target of JavaScript in mind,
3941it takes a slightly different approach, which is to reuse the existing data
4042types 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
4244needed before you can start operating on it. This ought to help your program
4345both in terms of speed and memory churn.
4446
4547Much of the design of Argonaut follows naturally from this design decision.
4648
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+
4768### Introducing Json values
4869
4970(Or, where do ` Json ` values come from?)
5071
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:
5478
5579``` javascript
5680// In an FFI module.
5781exports .someNumber = 23.6 ;
5882exports .someBoolean = false ;
59- exports .someObject = {people: [{name: " john" }, {name: " jane" }],
83+ exports .someObject = {people: [{name: " john" }, {name: " jane" }], common_interests : []};
6084```
6185
6286``` purescript
@@ -65,10 +89,9 @@ foreign import someBoolean :: Json
6589foreign import someObject :: Json
6690```
6791
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.
7295
7396The construction functions (that is, ` fromX ` , or ` jsonX ` ) can be used as
7497follows:
@@ -84,7 +107,8 @@ someObject = A.fromObject (StrMap.fromFoldable [
84107 Tuple "people" (A.fromArray [
85108 A.jsonSingletonObject "name" (A.fromString "john"),
86109 A.jsonSingletonObject "name" (A.fromString "jane")
87- ])
110+ ]),
111+ Tuple "common_interests" A.jsonEmptyArray
88112 ])
89113```
90114
@@ -96,21 +120,44 @@ This function is necessary because `Json` is not an algebraic data type. If
96120function, because we could perform pattern matching with a ` case ... of `
97121expression instead.
98122
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+
99146For example, imagine we had the following values defined in JavaScript and
100147imported via the FFI:
101148
102149``` 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 ]};
106153```
107154
108155Then we can match on them in PureScript using ` foldJson ` :
109156
110157``` 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
114161
115162basicInfo :: Json -> String
116163basicInfo = foldJson
@@ -127,29 +174,38 @@ basicInfo = foldJson
127174```
128175
129176``` 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."
133180```
134181
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:
141191
142192``` purescript
193+ foldJsonString :: forall a. a -> (JString -> a) -> Json -> a
194+
143195isJsonLol = foldJsonString false (_ == "lol")
144196```
145197
146198If the ` Json ` value is not a string, the default ` false ` is used. Otherwise,
147199we test whether the string is equal to "lol".
148200
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:
151205
152206``` purescript
207+ toString :: Json -> Maybe JString
208+
153209isJsonLol json =
154210 case toString json of
155211 Just str -> str == "lol"
0 commit comments