@@ -16,3 +16,198 @@ bower install purescript-argonaut-core
1616## Documentation
1717
1818Module 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 algebraic data
26+ type, like this:
27+
28+ ``` purescript
29+ data Json
30+ = JNull
31+ | JString String
32+ | JNumber Number
33+ | JBoolean Boolean
34+ | JArray (Array Json)
35+ | JObject (StrMap Json)
36+ ```
37+
38+ And indeed, some might even say this is the obvious approach.
39+
40+ Because Argonaut is written with the compilation target of JavaScript in mind,
41+ it takes a slightly different approach, which is to reuse the existing data
42+ types which JavaScript already provides. This way, the result of JavaScript's
43+ ` JSON.parse ` function is already a ` Json ` value, and no extra processing is
44+ needed before you can start operating on it. This ought to help your program
45+ both in terms of speed and memory churn.
46+
47+ Much of the design of Argonaut follows naturally from this design decision.
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+
68+ ### Introducing Json values
69+
70+ (Or, where do ` Json ` values come from?)
71+
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:
78+
79+ ``` javascript
80+ // In an FFI module.
81+ exports .someNumber = 23.6 ;
82+ exports .someBoolean = false ;
83+ exports .someObject = {people: [{name: " john" }, {name: " jane" }], common_interests: []};
84+ ```
85+
86+ ``` purescript
87+ foreign import someNumber :: Json
88+ foreign import someBoolean :: Json
89+ foreign import someObject :: Json
90+ ```
91+
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.
95+
96+ The construction functions (that is, ` fromX ` , or ` jsonX ` ) can be used as
97+ follows:
98+
99+ ``` purescript
100+ import Data.Tuple (Tuple(..))
101+ import Data.StrMap as StrMap
102+ import Data.Argonaut.Core as A
103+
104+ someNumber = A.fromNumber 23.6
105+ someBoolean = A.fromBoolean false
106+ someObject = A.fromObject (StrMap.fromFoldable [
107+ Tuple "people" (A.fromArray [
108+ A.jsonSingletonObject "name" (A.fromString "john"),
109+ A.jsonSingletonObject "name" (A.fromString "jane")
110+ ]),
111+ Tuple "common_interests" A.jsonEmptyArray
112+ ])
113+ ```
114+
115+ ### Eliminating/matching on ` Json ` values
116+
117+ We can perform case analysis for ` Json ` values using the ` foldJson ` function.
118+ This function is necessary because ` Json ` is not an algebraic data type. If
119+ ` Json ` were an algebraic data type, we would not have as much need for this
120+ function, because we could perform pattern matching with a ` case ... of `
121+ expression instead.
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+
146+ For example, imagine we had the following values defined in JavaScript and
147+ imported via the FFI:
148+
149+ ``` javascript
150+ exports .anotherNumber = 0.0 ;
151+ exports .anotherArray = [0.0 , {foo: ' bar' }, false ];
152+ exports .anotherObject = {foo: 1 , bar: [2 ,2 ]};
153+ ```
154+
155+ Then we can match on them in PureScript using ` foldJson ` :
156+
157+ ``` purescript
158+ foreign import anotherNumber :: Json
159+ foreign import anotherArray :: Json
160+ foreign import anotherObject :: Json
161+
162+ basicInfo :: Json -> String
163+ basicInfo = foldJson
164+ (const "It was null")
165+ (\b -> "Got a boolean: " <>
166+ if b then "it was true!" else "It was false.")
167+ (\x -> "Got a number: " <> show x)
168+ (\s -> "Got a string, which was " <> Data.String.length s <>
169+ " characters long.")
170+ (\xs -> "Got an array, which had " <> Data.Array.length xs <>
171+ " items.")
172+ (\obj -> "Got an object, which had " <> Data.StrMap.size obj <>
173+ " items.")
174+ ```
175+
176+ ``` purescript
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."
180+ ```
181+
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:
191+
192+ ``` purescript
193+ foldJsonString :: forall a. a -> (JString -> a) -> Json -> a
194+
195+ isJsonLol = foldJsonString false (_ == "lol")
196+ ```
197+
198+ If the ` Json ` value is not a string, the default ` false ` is used. Otherwise,
199+ we test whether the string is equal to "lol".
200+
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:
205+
206+ ``` purescript
207+ toString :: Json -> Maybe JString
208+
209+ isJsonLol json =
210+ case toString json of
211+ Just str -> str == "lol"
212+ Nothing -> false
213+ ```
0 commit comments