on master help sign in
Merge pull request #525 from Lysxia/comments

Reword comments about ToJSON and FromJSON

commit13ee589f9277061f57427ef39c22aa48b5231b5d authorAdam Bergmark <adam@bergmark.nl> dateTue 21 Mar 2017, 21:28 (10 years ago) parent253f1b9d591472
2 files changed+100−74
Data/Aeson/Types/FromJSON.hs+45−37 Data/Aeson/Types/ToJSON.hs+55−37
Data/Aeson/Types/FromJSON.hs+45−37
@@ -281,12 +281,12 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
281281--
282282-- The basic ways to signal a failed conversion are as follows:
283283--
284−-- * 'empty' and 'mzero' work, but are terse and uninformative
284+-- * 'empty' and 'mzero' work, but are terse and uninformative;
285285--
286−-- * 'fail' yields a custom error message
286+-- * 'fail' yields a custom error message;
287287--
288288-- * 'typeMismatch' produces an informative message for cases when the
289−-- value encountered is not of the expected type
289+-- value encountered is not of the expected type.
290290--
291291-- An example type and instance using 'typeMismatch':
292292--
@@ -296,27 +296,27 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
296296--
297297-- data Coord = Coord { x :: Double, y :: Double }
298298--
299−-- instance FromJSON Coord where
300−-- parseJSON ('Object' v) = Coord
301−-- '<$>' v '.:' \"x\"
299+-- instance 'FromJSON' Coord where
300+-- 'parseJSON' ('Object' v) = Coord
301+-- '<$>' v '.:' \"x\"
302302-- '<*>' v '.:' \"y\"
303303--
304304-- \-- We do not expect a non-'Object' value here.
305305-- \-- We could use 'mzero' to fail, but 'typeMismatch'
306306-- \-- gives a much more informative error message.
307−-- parseJSON invalid = 'typeMismatch' \"Coord\" invalid
307+-- 'parseJSON' invalid = 'typeMismatch' \"Coord\" invalid
308308-- @
309309--
310310-- For this common case of only being concerned with a single
311−-- type of JSON value, the functions @withObject@, @withNumber@, etc.
311+-- type of JSON value, the functions 'withObject', 'withNumber', etc.
312312-- are provided. Their use is to be preferred when possible, since
313−-- they are more terse. Using @withObject@, we can rewrite the above instance
313+-- they are more terse. Using 'withObject', we can rewrite the above instance
314314-- (assuming the same language extension and data type) as:
315315--
316316-- @
317−-- instance FromJSON Coord where
318−-- parseJSON = withObject \"Coord\" $ \v -> Coord
319−-- '<$>' v '.:' \"x\"
317+-- instance 'FromJSON' Coord where
318+-- 'parseJSON' = 'withObject' \"Coord\" $ \v -> Coord
319+-- '<$>' v '.:' \"x\"
320320-- '<*>' v '.:' \"y\"
321321-- @
322322--
@@ -325,7 +325,7 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
325325--
326326-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
327327-- instance at compile time. The generated instance is optimized for your type
328−-- so will probably be more efficient than the following two options:
328+-- so it will probably be more efficient than the following option.
329329--
330330-- * The compiler can provide a default generic implementation for
331331-- 'parseJSON'.
@@ -343,18 +343,21 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
343343--
344344-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
345345--
346−-- instance FromJSON Coord
346+-- instance 'FromJSON' Coord
347347-- @
348348--
349−-- If @DefaultSignatures@ doesn't give exactly the results you want,
350−-- you can customize the generic decoding with only a tiny amount of
351−-- effort, using 'genericParseJSON' with your preferred 'Options':
349+-- The default implementation will be equivalent to
350+-- @parseJSON = 'genericParseJSON' 'defaultOptions'@; If you need different
351+-- options, you can customize the generic decoding by defining:
352352--
353353-- @
354−-- instance FromJSON Coord where
355−-- parseJSON = 'genericParseJSON' 'defaultOptions'
354+-- customOptions = 'defaultOptions'
355+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
356+-- }
357+--
358+-- instance 'FromJSON' Coord where
359+-- 'parseJSON' = 'genericParseJSON' customOptions
356360-- @
357−
358361class FromJSON a where
359362 parseJSON :: Value -> Parser a
360363
@@ -516,7 +519,7 @@ typeMismatch expected actual =
516519--
517520-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
518521-- instance at compile time. The generated instance is optimized for your type
519−-- so will probably be more efficient than the following two options:
522+-- so it will probably be more efficient than the following option.
520523--
521524-- * The compiler can provide a default generic implementation for
522525-- 'liftParseJSON'.
@@ -534,16 +537,20 @@ typeMismatch expected actual =
534537--
535538-- data Pair a b = Pair { pairFst :: a, pairSnd :: b } deriving 'Generic1'
536539--
537−-- instance FromJSON a => FromJSON1 (Pair a)
540+-- instance 'FromJSON' a => 'FromJSON1' (Pair a)
538541-- @
539542--
540−-- If @DefaultSignatures@ doesn't give exactly the results you want,
543+-- If the default implementation doesn't give exactly the results you want,
541544-- you can customize the generic decoding with only a tiny amount of
542545-- effort, using 'genericLiftParseJSON' with your preferred 'Options':
543546--
544547-- @
545−-- instance FromJSON a => FromJSON1 (Pair a) where
546−-- liftParseJSON = 'genericLiftParseJSON' 'defaultOptions'
548+-- customOptions = 'defaultOptions'
549+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
550+-- }
551+--
552+-- instance 'FromJSON' a => 'FromJSON1' (Pair a) where
553+-- 'liftParseJSON' = 'genericLiftParseJSON' customOptions
547554-- @
548555class FromJSON1 f where
549556 liftParseJSON :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a)
@@ -614,43 +621,44 @@ instance (FromJSON a) => FromJSON [a] where
614621-- Functions
615622-------------------------------------------------------------------------------
616623
617−-- | @withObject expected f value@ applies @f@ to the 'Object' when @value@ is an @Object@
618−-- and fails using @'typeMismatch' expected@ otherwise.
624+-- | @'withObject' expected f value@ applies @f@ to the 'Object' when @value@
625+-- is an 'Object' and fails using @'typeMismatch' expected@ otherwise.
619626withObject :: String -> (Object -> Parser a) -> Value -> Parser a
620627withObject _ f (Object obj) = f obj
621628withObject expected _ v = typeMismatch expected v
622629{-# INLINE withObject #-}
623630
624−-- | @withText expected f value@ applies @f@ to the 'Text' when @value@ is a @String@
625−-- and fails using @'typeMismatch' expected@ otherwise.
631+-- | @'withText' expected f value@ applies @f@ to the 'Text' when @value@ is a
632+-- 'String' and fails using @'typeMismatch' expected@ otherwise.
626633withText :: String -> (Text -> Parser a) -> Value -> Parser a
627634withText _ f (String txt) = f txt
628635withText expected _ v = typeMismatch expected v
629636{-# INLINE withText #-}
630637
631−-- | @withArray expected f value@ applies @f@ to the 'Array' when @value@ is an @Array@
632−-- and fails using @'typeMismatch' expected@ otherwise.
638+-- | @'withArray' expected f value@ applies @f@ to the 'Array' when @value@ is
639+-- an 'Array' and fails using @'typeMismatch' expected@ otherwise.
633640withArray :: String -> (Array -> Parser a) -> Value -> Parser a
634641withArray _ f (Array arr) = f arr
635642withArray expected _ v = typeMismatch expected v
636643{-# INLINE withArray #-}
637644
638−-- | @withNumber expected f value@ applies @f@ to the 'Number' when @value@ is a 'Number'.
639−-- and fails using @'typeMismatch' expected@ otherwise.
645+-- | @'withNumber' expected f value@ applies @f@ to the 'Number' when @value@
646+-- is a 'Number' and fails using @'typeMismatch' expected@ otherwise.
640647withNumber :: String -> (Number -> Parser a) -> Value -> Parser a
641648withNumber expected f = withScientific expected (f . scientificToNumber)
642649{-# INLINE withNumber #-}
643650{-# DEPRECATED withNumber "Use withScientific instead" #-}
644651
645−-- | @withScientific expected f value@ applies @f@ to the 'Scientific' number when @value@ is a 'Number'.
646−-- and fails using @'typeMismatch' expected@ otherwise.
652+-- | @'withScientific' expected f value@ applies @f@ to the 'Scientific' number
653+-- when @value@ is a 'Number' and fails using @'typeMismatch' expected@
654+-- otherwise.
647655withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
648656withScientific _ f (Number scientific) = f scientific
649657withScientific expected _ v = typeMismatch expected v
650658{-# INLINE withScientific #-}
651659
652−-- | @withBool expected f value@ applies @f@ to the 'Bool' when @value@ is a @Bool@
653−-- and fails using @'typeMismatch' expected@ otherwise.
660+-- | @'withBool' expected f value@ applies @f@ to the 'Bool' when @value@ is a
661+-- 'Bool' and fails using @'typeMismatch' expected@ otherwise.
654662withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
655663withBool _ f (Bool arr) = f arr
656664withBool expected _ v = typeMismatch expected v
Data/Aeson/Types/ToJSON.hs+55−37
@@ -205,6 +205,9 @@ genericLiftToEncoding opts te tel = gToEncoding opts (To1Args te tel) . from1
205205
206206-- | A type that can be converted to JSON.
207207--
208+-- Instances in general /must/ specify 'toJSON' and /should/ (but don't need
209+-- to) specify 'toEncoding'.
210+--
208211-- An example type and instance:
209212--
210213-- @
@@ -213,10 +216,10 @@ genericLiftToEncoding opts te tel = gToEncoding opts (To1Args te tel) . from1
213216--
214217-- data Coord = Coord { x :: Double, y :: Double }
215218--
216−-- instance ToJSON Coord where
217−-- toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
219+-- instance 'ToJSON' Coord where
220+-- 'toJSON' (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
218221--
219−-- toEncoding (Coord x y) = 'pairs' (\"x\" '.=' x '<>' \"y\" '.=' y)
222+-- 'toEncoding' (Coord x y) = 'pairs' (\"x\" '.=' x '<>' \"y\" '.=' y)
220223-- @
221224--
222225-- Instead of manually writing your 'ToJSON' instance, there are two options
@@ -224,17 +227,15 @@ genericLiftToEncoding opts te tel = gToEncoding opts (To1Args te tel) . from1
224227--
225228-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
226229-- instance at compile time. The generated instance is optimized for your type
227−-- so will probably be more efficient than the following two options:
230+-- so it will probably be more efficient than the following option.
228231--
229232-- * The compiler can provide a default generic implementation for
230233-- 'toJSON'.
231234--
232235-- To use the second, simply add a @deriving 'Generic'@ clause to your
233−-- datatype and declare a 'ToJSON' instance for your datatype without giving
234−-- definitions for 'toJSON' or 'toEncoding'.
235−--
236−-- For example, the previous example can be simplified to a more
237−-- minimal instance:
236+-- datatype and declare a 'ToJSON' instance. If you require nothing other than
237+-- 'defaultOptions', it is sufficient to write (and this is the only
238+-- alternative where the default 'toJSON' implementation is sufficient):
238239--
239240-- @
240241-- {-\# LANGUAGE DeriveGeneric \#-}
@@ -243,30 +244,41 @@ genericLiftToEncoding opts te tel = gToEncoding opts (To1Args te tel) . from1
243244--
244245-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
245246--
246−-- instance ToJSON Coord where
247−-- toEncoding = 'genericToEncoding' 'defaultOptions'
247+-- instance 'ToJSON' Coord where
248+-- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
248249-- @
249250--
250−-- Why do we provide an implementation for 'toEncoding' here? The
251−-- 'toEncoding' function is a relatively new addition to this class.
252−-- To allow users of older versions of this library to upgrade without
253−-- having to edit all of their instances or encounter surprising
254−-- incompatibilities, the default implementation of 'toEncoding' uses
255−-- 'toJSON'. This produces correct results, but since it performs an
256−-- intermediate conversion to a 'Value', it will be less efficient
257−-- than directly emitting an 'Encoding'. Our one-liner definition of
258−-- 'toEncoding' above bypasses the intermediate 'Value'.
259−--
260−-- If @DefaultSignatures@ doesn't give exactly the results you want,
261−-- you can customize the generic encoding with only a tiny amount of
262−-- effort, using 'genericToJSON' and 'genericToEncoding' with your
263−-- preferred 'Options':
251+-- If on the other hand you wish to customize the generic decoding, you have
252+-- to implement both methods:
264253--
265254-- @
266−-- instance ToJSON Coord where
267−-- toJSON = 'genericToJSON' 'defaultOptions'
268−-- toEncoding = 'genericToEncoding' 'defaultOptions'
255+-- customOptions = 'defaultOptions'
256+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
257+-- }
258+--
259+-- instance 'ToJSON' Coord where
260+-- 'toJSON' = 'genericToJSON' customOptions
261+-- 'toEncoding' = 'genericToEncoding' customOptions
269262-- @
263+--
264+-- Previous versions of this library only had the 'toJSON' method. Adding
265+-- 'toEncoding' had to reasons:
266+--
267+-- 1. toEncoding is more efficient for the common case that the output of
268+-- 'toJSON' is directly serialized to a @ByteString@.
269+-- Further, expressing either method in terms of the other would be
270+-- non-optimal.
271+--
272+-- 2. The choice of defaults allows a smooth transition for existing users:
273+-- Existing instances that do not define 'toEncoding' still
274+-- compile and have the correct semantics. This is ensured by making
275+-- the default implementation of 'toEncoding' use 'toJSON'. This produces
276+-- correct results, but since it performs an intermediate conversion to a
277+-- 'Value', it will be less efficient than directly emitting an 'Encoding'.
278+-- (this also means that specifying nothing more than
279+-- @instance ToJSON Coord@ would be sufficient as a generically decoding
280+-- instance, but there probably exists no good reason to not specify
281+-- 'toEncoding' in new instances.)
270282class ToJSON a where
271283 -- | Convert a Haskell value to a JSON-friendly intermediate type.
272284 toJSON :: a -> Value
@@ -288,8 +300,8 @@ class ToJSON a where
288300 -- extension, and then have GHC generate a method body as follows.
289301 --
290302 -- @
291− -- instance ToJSON Coord where
292− -- toEncoding = 'genericToEncoding' 'defaultOptions'
303+ -- instance 'ToJSON' Coord where
304+ -- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
293305 -- @
294306
295307 toEncoding :: a -> Encoding
@@ -491,7 +503,7 @@ contramapToJSONKeyFunction h x = case x of
491503--
492504-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
493505-- instance at compile time. The generated instance is optimized for your type
494−-- so will probably be more efficient than the following two options:
506+-- so it will probably be more efficient than the following option.
495507--
496508-- * The compiler can provide a default generic implementation for
497509-- 'toJSON1'.
@@ -509,19 +521,25 @@ contramapToJSONKeyFunction h x = case x of
509521--
510522-- data Pair = Pair { pairFst :: a, pairSnd :: b } deriving 'Generic1'
511523--
512−-- instance ToJSON a => ToJSON1 (Pair a)
524+-- instance 'ToJSON' a => 'ToJSON1' (Pair a)
513525-- @
514526--
515−-- If @DefaultSignatures@ doesn't give exactly the results you want,
527+-- If the default implementation doesn't give exactly the results you want,
516528-- you can customize the generic encoding with only a tiny amount of
517529-- effort, using 'genericLiftToJSON' and 'genericLiftToEncoding' with
518530-- your preferred 'Options':
519531--
520532-- @
521−-- instance ToJSON a => ToJSON1 (Pair a) where
522−-- liftToJSON = 'genericLiftToJSON' 'defaultOptions'
523−-- liftToEncoding = 'genericLiftToEncoding' 'defaultOptions'
533+-- customOptions = 'defaultOptions'
534+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
535+-- }
536+--
537+-- instance 'ToJSON' a => 'ToJSON1' (Pair a) where
538+-- 'liftToJSON' = 'genericLiftToJSON' customOptions
539+-- 'liftToEncoding' = 'genericLiftToEncoding' customOptions
524540-- @
541+--
542+-- See also 'ToJSON'.
525543class ToJSON1 f where
526544 liftToJSON :: (a -> Value) -> ([a] -> Value) -> f a -> Value
527545
@@ -588,7 +606,7 @@ toEncoding2 = liftToEncoding2 toEncoding toEncodingList toEncoding toEncodingLis
588606-- @
589607-- newtype F a = F [a]
590608--
591−-- -- This instance encodes String as an array of chars
609+-- -- This instance encodes 'String' as an array of chars
592610-- instance 'ToJSON1' F where
593611-- 'liftToJSON' tj _ (F xs) = 'liftToJSON' tj ('listValue' tj) xs
594612-- 'liftToEncoding' te _ (F xs) = 'liftToEncoding' te ('listEncoding' te) xs