Skip to content

Commit 13ee589

Browse files
authored
Merge pull request #525 from Lysxia/comments
Reword comments about ToJSON and FromJSON
2 parents 253f1b9 + d591472 commit 13ee589

File tree

2 files changed

+100
-74
lines changed

2 files changed

+100
-74
lines changed

Data/Aeson/Types/FromJSON.hs

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
358361
class 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
-- @
548555
class 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.
619626
withObject :: String -> (Object -> Parser a) -> Value -> Parser a
620627
withObject _ f (Object obj) = f obj
621628
withObject 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.
626633
withText :: String -> (Text -> Parser a) -> Value -> Parser a
627634
withText _ f (String txt) = f txt
628635
withText 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.
633640
withArray :: String -> (Array -> Parser a) -> Value -> Parser a
634641
withArray _ f (Array arr) = f arr
635642
withArray 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.
640647
withNumber :: String -> (Number -> Parser a) -> Value -> Parser a
641648
withNumber 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.
647655
withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
648656
withScientific _ f (Number scientific) = f scientific
649657
withScientific 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.
654662
withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
655663
withBool _ f (Bool arr) = f arr
656664
withBool expected _ v = typeMismatch expected v

Data/Aeson/Types/ToJSON.hs

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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,28 +216,26 @@ 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
223226
-- to do it automatically:
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.)
270282
class 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'.
525543
class 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

0 commit comments

Comments
 (0)