on master help sign in
Merge pull request #412 from phadej/abstract-encoding-2

Abstract `Encoding`

commit26a4bfbab631cf641eb34d45d9eadbc4a1b28a36 authorAdam Bergmark <adam@bergmark.nl> dateMon 6 Jun 2016, 12:13 (10 years ago) parente5194f4a523d02
15 files changed+461−408
Data/Aeson/Encode.hs+12−2 Data/Aeson/Encode/Builder.hs+8−14 Data/Aeson/Encode/Functions.hs+3−67 Data/Aeson/Encoding.hs+19 Data/Aeson/Encoding/Internal.hs+166 Data/Aeson/TH.hs+15−16 Data/Aeson/Types.hs+1−6 Data/Aeson/Types/Class.hs+16−17 Data/Aeson/Types/Generic.hs+52−57 Data/Aeson/Types/Instances.hs+40−41 Data/Aeson/Types/Instances/Tuple.hs+121−134 Data/Aeson/Types/Internal.hs−50 aeson.cabal+2 benchmarks/aeson-benchmarks.cabal+2 tuple-instances.hs+4−4
Data/Aeson/Encode.hs+12−2
@@ -28,16 +28,26 @@ module Data.Aeson.Encode
2828 ) where
2929
3030import Data.Aeson.Encode.Builder (encodeToBuilder)
31−import Data.Aeson.Encode.Functions (encode)
32−import Data.Aeson.Types (Value(..))
31+import Data.Aeson.Types (Value(..), ToJSON (..))
32+import Data.Aeson.Encoding (fromEncoding)
3333import Data.Monoid ((<>))
3434import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
3535import Data.Text.Lazy.Builder
3636import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
37+
3738import Numeric (showHex)
39+import qualified Data.ByteString.Builder as B
3840import qualified Data.HashMap.Strict as H
3941import qualified Data.Text as T
4042import qualified Data.Vector as V
43+import qualified Data.ByteString.Lazy as L
44+
45+-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
46+--
47+-- This is implemented in terms of the 'ToJSON' class's 'toEncoding' method.
48+encode :: ToJSON a => a -> L.ByteString
49+encode = B.toLazyByteString . fromEncoding . toEncoding
50+{-# INLINE encode #-}
4151
4252-- | Encode a JSON 'Value' to a "Data.Text" 'Builder', which can be
4353-- embedded efficiently in a text-based protocol.
Data/Aeson/Encode/Builder.hs+8−14
@@ -36,7 +36,7 @@ module Data.Aeson.Encode.Builder
3636 ) where
3737
3838import Data.Aeson.Internal.Time
39−import Data.Aeson.Types.Internal (Encoding(..), Value(..))
39+import Data.Aeson.Types.Internal (Value (..))
4040import Data.ByteString.Builder as B
4141import Data.ByteString.Builder.Prim as BP
4242import Data.ByteString.Builder.Scientific (scientificBuilder)
@@ -76,7 +76,7 @@ bool = BP.primBounded (BP.condB id (ascii4 ('t',('r',('u','e'))))
7676-- | Encode a JSON array.
7777array :: V.Vector Value -> Builder
7878array v
79− | V.null v = emptyArray__
79+ | V.null v = emptyArray_
8080 | otherwise = B.char8 '[' <>
8181 encodeToBuilder (V.unsafeHead v) <>
8282 V.foldr withComma (B.char8 ']') (V.unsafeTail v)
@@ -87,7 +87,7 @@ array v
8787object :: HMS.HashMap T.Text Value -> Builder
8888object m = case HMS.toList m of
8989 (x:xs) -> B.char8 '{' <> one x <> foldr withComma (B.char8 '}') xs
90− _ -> emptyObject__
90+ _ -> emptyObject_
9191 where
9292 withComma a z = B.char8 ',' <> one a <> z
9393 one (k,v) = text k <> B.char8 ':' <> encodeToBuilder v
@@ -98,7 +98,7 @@ text t = B.char8 '"' <> unquoted t <> B.char8 '"'
9898
9999-- | Encode a JSON string, without enclosing quotes.
100100unquoted :: T.Text -> Builder
101−unquoted t = TE.encodeUtf8BuilderEscaped escapeAscii t
101+unquoted = TE.encodeUtf8BuilderEscaped escapeAscii
102102
103103-- | Add quotes surrounding a builder
104104quote :: Builder -> Builder
@@ -135,17 +135,11 @@ number s
135135 where
136136 e = base10Exponent s
137137
138−emptyArray_ :: Encoding
139−emptyArray_ = Encoding emptyArray__
138+emptyArray_ :: Builder
139+emptyArray_ = BP.primBounded (ascii2 ('[',']')) ()
140140
141−emptyArray__ :: Builder
142−emptyArray__ = BP.primBounded (ascii2 ('[',']')) ()
143−
144−emptyObject_ :: Encoding
145−emptyObject_ = Encoding emptyObject__
146−
147−emptyObject__ :: Builder
148−emptyObject__ = BP.primBounded (ascii2 ('{','}')) ()
141+emptyObject_ :: Builder
142+emptyObject_ = BP.primBounded (ascii2 ('{','}')) ()
149143
150144ascii2 :: (Char, Char) -> BP.BoundedPrim a
151145ascii2 cs = BP.liftFixedToBounded $ (const cs) BP.>$< BP.char7 >*< BP.char7
Data/Aeson/Encode/Functions.hs+3−67
@@ -2,78 +2,14 @@
22
33module Data.Aeson.Encode.Functions
44 (
5− brackets
6− , builder
7− , builder'
8− , char7
9− , encode
10− , list
11− , encodeMap
12− , encodeWithKey
5+ builder
136 ) where
147
15−import Data.Aeson.Encode.Builder
8+import Data.Aeson.Encoding
169import Data.Aeson.Types.Class
17−import Data.Aeson.Types.Internal
18−import Data.ByteString.Builder (Builder, char7)
19−import Data.ByteString.Builder.Prim (primBounded)
20−import Data.Foldable (toList)
10+import Data.ByteString.Builder (Builder)
2111import Data.Monoid ((<>))
22−import qualified Data.ByteString.Builder as B
23−import qualified Data.ByteString.Lazy as L
24−
25−#if !MIN_VERSION_base(4,8,0)
26−import Data.Foldable (Foldable, foldMap)
27−import Data.Monoid (mempty)
28−#endif
29−
30−list :: (a -> Encoding) -> [a] -> Encoding
31−list = listEncoding
3212
3313builder :: ToJSON a => a -> Builder
3414builder = fromEncoding . toEncoding
3515{-# INLINE builder #-}
36−
37−builder' :: (a -> Encoding) -> a -> Builder
38−builder' f = fromEncoding . f
39−{-# INLINE builder' #-}
40−
41−-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
42−--
43−-- This is implemented in terms of the 'ToJSON' class's 'toEncoding' method.
44−encode :: ToJSON a => a -> L.ByteString
45−encode = B.toLazyByteString . builder
46−{-# INLINE encode #-}
47−
48−brackets :: Char -> Char -> Series -> Encoding
49−brackets begin end (Value v) = Encoding $
50− char7 begin <> fromEncoding v <> char7 end
51−brackets begin end Empty = Encoding (primBounded (ascii2 (begin,end)) ())
52−
53−
54−
55−encodeMap :: (k -> Encoding)
56− -> (v -> Encoding)
57− -> (m -> Maybe ((k,v), m))
58− -> ((k -> v -> B.Builder -> B.Builder) -> B.Builder -> m -> B.Builder)
59− -> m -> Encoding
60−encodeMap encodeKey encodeVal minViewWithKey foldrWithKey xs =
61− case minViewWithKey xs of
62− Nothing -> Encoding $ primBounded (ascii2 ('{', '}')) ()
63− Just ((k,v),ys) -> Encoding $
64− B.char7 '{' <> encodeKV encodeKey encodeVal k v <>
65− foldrWithKey go (B.char7 '}') ys
66− where go k v b = B.char7 ',' <> encodeKV encodeKey encodeVal k v <> b
67−{-# INLINE encodeMap #-}
68−
69−encodeWithKey :: (k -> Encoding)
70− -> (v -> Encoding)
71− -> ((k -> v -> Series -> Series) -> Series -> m -> Series)
72− -> m -> Encoding
73−encodeWithKey encodeKey encodeVal foldrWithKey = brackets '{' '}' . foldrWithKey go mempty
74− where go k v c = Value (Encoding $ encodeKV encodeKey encodeVal k v) <> c
75−{-# INLINE encodeWithKey #-}
76−
77−encodeKV :: (k -> Encoding) -> (v -> Encoding) -> k -> v -> B.Builder
78−encodeKV encodeKey encodeVal k v = fromEncoding (encodeKey k) <> B.char7 ':' <> builder' encodeVal v
79−{-# INLINE encodeKV #-}
Data/Aeson/Encoding.hsnew file+19
@@ -0,0 +1,19 @@
1+module Data.Aeson.Encoding
2+ (
3+ -- * Encoding
4+ Encoding
5+ , fromEncoding
6+ , unsafeToEncoding
7+ , Series
8+ , pairs
9+ -- * Predicates
10+ , nullEncoding
11+ -- * Encoding constructors
12+ , emptyArray_
13+ , emptyObject_
14+ , text
15+ , list
16+ , dict
17+ ) where
18+
19+import Data.Aeson.Encoding.Internal
Data/Aeson/Encoding/Internal.hsnew file+166
@@ -0,0 +1,166 @@
1+{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, DeriveDataTypeable, RankNTypes #-}
2+module Data.Aeson.Encoding.Internal
3+ (
4+ -- * Encoding
5+ Encoding (..) -- TODO: export fromEncoding for now
6+ , fromEncoding
7+ , unsafeToEncoding
8+ , Series (..) -- TODO: don't export constructor
9+ , pairs
10+ -- * Predicates
11+ , nullEncoding
12+ -- * Encoding constructors
13+ , emptyArray_
14+ , emptyObject_
15+ , wrapArray
16+ , wrapObject
17+ , text
18+ , list
19+ , dict
20+ , tuple
21+ , (>*<)
22+ -- ** chars
23+ , comma, colon, openBracket, closeBracket, openCurly, closeCurly
24+ ) where
25+
26+#if !MIN_VERSION_base(4,8,0)
27+import Data.Monoid (Monoid(..))
28+#endif
29+
30+import Data.Text (Text)
31+import Data.ByteString.Builder (Builder, char7, toLazyByteString)
32+import Data.Semigroup (Semigroup((<>)))
33+import Data.ByteString.Builder.Prim (primBounded)
34+import Data.Typeable (Typeable)
35+
36+import qualified Data.ByteString.Lazy as BSL
37+import qualified Data.Aeson.Encode.Builder as B
38+
39+-- | An encoding of a JSON value.
40+newtype Encoding = Encoding {
41+ fromEncoding :: Builder
42+ -- ^ Acquire the underlying bytestring builder.
43+ } deriving (Semigroup,Monoid,Typeable)
44+
45+-- | Make Encoding from Builder.
46+--
47+-- Use with care! You have to make sure that the passed Builder
48+-- is a valid JSON Encoding!
49+unsafeToEncoding :: Builder -> Encoding
50+unsafeToEncoding = Encoding
51+
52+-------------------------------------------------------------------------------
53+-- Encoding instances
54+-------------------------------------------------------------------------------
55+
56+instance Show Encoding where
57+ show (Encoding e) = show (toLazyByteString e)
58+
59+instance Eq Encoding where
60+ Encoding a == Encoding b = toLazyByteString a == toLazyByteString b
61+
62+instance Ord Encoding where
63+ compare (Encoding a) (Encoding b) =
64+ compare (toLazyByteString a) (toLazyByteString b)
65+
66+-- | A series of values that, when encoded, should be separated by
67+-- commas. Since 0.11.0.0, the '.=' operator is overloaded to create
68+-- either @(Text, Value)@ or 'Series'. You can use Series when
69+-- encoding directly to a bytestring builder as in the following
70+-- example:
71+--
72+-- > toEncoding (Person name age) = pairs ("name" .= name <> "age" .= age)
73+data Series = Empty
74+ | Value Encoding
75+ deriving (Typeable)
76+
77+instance Semigroup Series where
78+ Empty <> a = a
79+ Value a <> b =
80+ Value $
81+ a <> case b of
82+ Empty -> mempty
83+ Value c -> Encoding (char7 ',') <> c
84+
85+instance Monoid Series where
86+ mempty = Empty
87+ mappend = (<>)
88+
89+nullEncoding :: Encoding -> Bool
90+nullEncoding = BSL.null . toLazyByteString . fromEncoding
91+
92+emptyArray_ :: Encoding
93+emptyArray_ = Encoding B.emptyArray_
94+
95+emptyObject_ :: Encoding
96+emptyObject_ = Encoding B.emptyObject_
97+
98+wrapArray :: Encoding -> Encoding
99+wrapArray e = openBracket <> e <> closeBracket
100+
101+wrapObject :: Encoding -> Encoding
102+wrapObject e = openCurly <> e <> closeCurly
103+
104+-- | Encode a series of key/value pairs, separated by commas.
105+pairs :: Series -> Encoding
106+pairs = brackets '{' '}'
107+{-# INLINE pairs #-}
108+
109+brackets :: Char -> Char -> Series -> Encoding
110+brackets begin end (Value v) = Encoding $
111+ char7 begin <> fromEncoding v <> char7 end
112+brackets begin end Empty = Encoding (primBounded (B.ascii2 (begin,end)) ())
113+
114+list :: (a -> Encoding) -> [a] -> Encoding
115+list _ [] = emptyArray_
116+list to' (x:xs) = Encoding $
117+ char7 '[' <> fromEncoding (to' x) <> commas xs <> char7 ']'
118+ where
119+ commas = foldr (\v vs -> char7 ',' <> fromEncoding (to' v) <> vs) mempty
120+{-# INLINE list #-}
121+
122+-- | Encode as JSON object
123+dict
124+ :: (k -> Encoding) -- ^ key encoding
125+ -> (v -> Encoding) -- ^ value encoding
126+ -> (forall a. (k -> v -> a -> a) -> a -> m -> a) -- ^ @foldrWithKey@ - indexed fold
127+ -> m -- ^ container
128+ -> Encoding
129+dict encodeKey encodeVal foldrWithKey = brackets '{' '}' . foldrWithKey go mempty
130+ where
131+ go k v c = Value (encodeKV k v) <> c
132+ encodeKV k v = encodeKey k <> Encoding (char7 ':') <> encodeVal v
133+{-# INLINE dict #-}
134+
135+infixr 6 >*<
136+-- | See 'tuple'.
137+(>*<) :: Encoding -> Encoding -> Encoding
138+Encoding a >*< Encoding b = Encoding $ a <> char7 ',' <> b
139+{-# INLINE (>*<) #-}
140+
141+-- | Encode as a tuple.
142+--
143+-- @
144+-- toEncoding (X a b c) = tuple $
145+-- toEncoding a >*<
146+-- toEncoding b >*<
147+-- toEncoding c
148+tuple :: Encoding -> Encoding
149+tuple b = Encoding (char7 '[' <> fromEncoding b <> char7 ']')
150+{-# INLINE tuple #-}
151+
152+text :: Text -> Encoding
153+text = Encoding . B.text
154+
155+-------------------------------------------------------------------------------
156+-- chars
157+-------------------------------------------------------------------------------
158+
159+comma, colon, openBracket, closeBracket, openCurly, closeCurly :: Encoding
160+comma = Encoding $ char7 ','
161+colon = Encoding $ char7 ':'
162+openBracket = Encoding $ char7 '['
163+closeBracket = Encoding $ char7 ']'
164+openCurly = Encoding $ char7 '{'
165+closeCurly = Encoding $ char7 '}'
166+
Data/Aeson/TH.hs+15−16
@@ -107,7 +107,7 @@ import Data.Aeson.Types ( Value(..), Parser
107107 , defaultOptions
108108 , defaultTaggedObject
109109 )
110−import Data.Aeson.Types.Internal (Encoding(..), (<?>), JSONPathElement(Key))
110+import Data.Aeson.Types.Internal ((<?>), JSONPathElement(Key))
111111import Control.Monad ( liftM2, return, mapM, fail )
112112import Data.Bool ( Bool(False, True), otherwise, (&&), not )
113113import Data.Either ( Either(Left, Right) )
@@ -140,8 +140,7 @@ import Prelude ( head )
140140import Text.Printf ( printf )
141141import Text.Show ( show )
142142import qualified Data.Aeson as A
143−import qualified Data.Aeson.Encode.Builder as E
144−import qualified Data.Aeson.Encode.Functions as E
143+import qualified Data.Aeson.Encoding.Internal as E
145144import qualified Data.HashMap.Strict as H ( lookup, toList )
146145import qualified Data.Map as M ( fromList, findWithDefault )
147146import qualified Data.Text as T ( Text, pack, unpack )
@@ -291,7 +290,7 @@ consToEncoding opts cons = do
291290 matches
292291 | allNullaryToStringTag opts && all isNullary cons =
293292 [ match (conP conName [])
294− (normalB $ [|Encoding|] `appE` encStr opts conName) []
293+ (normalB $ encStr opts conName) []
295294 | con <- cons
296295 , let conName = getConName con
297296 ]
@@ -458,23 +457,23 @@ isMaybe _ = False
458457infixr 6 <^>
459458
460459(<:>) :: ExpQ -> ExpQ -> ExpQ
461−(<:>) a b = a <^> [|E.char7 ':'|] <^> b
460+(<:>) a b = a <^> [|E.colon|] <^> b
462461infixr 5 <:>
463462
464463(<%>) :: ExpQ -> ExpQ -> ExpQ
465−(<%>) a b = a <^> [|E.char7 ','|] <^> b
464+(<%>) a b = a <^> [|E.comma|] <^> b
466465infixr 4 <%>
467466
468467array :: ExpQ -> ExpQ
469−array exp = [|Encoding|] `appE` ([|E.char7 '['|] <^> exp <^> [|E.char7 ']'|])
468+array exp = [|E.wrapArray|] `appE` exp
470469
471470object :: ExpQ -> ExpQ
472−object exp = [|Encoding|] `appE` ([|E.char7 '{'|] <^> exp <^> [|E.char7 '}'|])
471+object exp = [|E.wrapObject|] `appE` exp
473472
474473sumToEncoding :: Options -> Bool -> Name -> Q Exp -> Q Exp
475474sumToEncoding opts multiCons conName exp
476475 | multiCons =
477− let fexp = [|fromEncoding|] `appE` exp in
476+ let fexp = exp in
478477 case sumEncoding opts of
479478 TwoElemArray ->
480479 array (encStr opts conName <%> fexp)
@@ -514,7 +513,7 @@ argsToEncoding opts multiCons (NormalC conName ts) = do
514513 [e] -> return ([|toEncoding|] `appE` varE e)
515514 -- Multiple arguments are converted to a JSON array.
516515 es ->
517− return (array (foldr1 (<%>) [[|E.builder|] `appE` varE x | x <- es]))
516+ return (array (foldr1 (<%>) [[|toEncoding|] `appE` varE x | x <- es]))
518517 match (conP conName $ map varP args)
519518 (normalB $ sumToEncoding opts multiCons conName js)
520519 []
@@ -528,7 +527,7 @@ argsToEncoding opts multiCons (RecC conName ts) = case (unwrapUnaryRecords opts,
528527 let exp = object objBody
529528
530529 objBody = [|mconcat|] `appE`
531− ([|intersperse (E.char7 ',')|] `appE` pairs)
530+ ([|intersperse E.comma|] `appE` pairs)
532531 pairs | omitNothingFields opts = infixApp maybeFields
533532 [|(<>)|]
534533 restFields
@@ -546,16 +545,16 @@ argsToEncoding opts multiCons (RecC conName ts) = case (unwrapUnaryRecords opts,
546545 infixApp
547546 (infixApp
548547 (infixE
549− (Just $ toFieldName field <^> [|E.char7 ':'|])
548+ (Just $ toFieldName field <^> [|E.colon|])
550549 [|(<>)|]
551550 Nothing)
552551 [|(.)|]
553− [|E.builder|])
552+ [|toEncoding|])
554553 [|(<$>)|]
555554 (varE arg)
556555
557556 toPair (arg, (field, _, _)) =
558− toFieldName field <:> [|E.builder|] `appE` varE arg
557+ toFieldName field <:> [|toEncoding|] `appE` varE arg
559558
560559 toFieldName field = [|E.text|] `appE`
561560 ([|T.pack|] `appE` fieldLabelExp opts field)
@@ -565,13 +564,13 @@ argsToEncoding opts multiCons (RecC conName ts) = case (unwrapUnaryRecords opts,
565564 $ if multiCons
566565 then case sumEncoding opts of
567566 TwoElemArray -> array $
568− encStr opts conName <%> [|fromEncoding|] `appE` exp
567+ encStr opts conName <%> exp
569568 TaggedObject{tagFieldName} -> object $
570569 ([|E.text (T.pack tagFieldName)|] <:>
571570 encStr opts conName) <%>
572571 objBody
573572 ObjectWithSingleField -> object $
574− encStr opts conName <:> [|fromEncoding|] `appE` exp
573+ encStr opts conName <:> exp
575574 else exp
576575 ) []
577576
Data/Aeson/Types.hs+1−6
@@ -97,16 +97,11 @@ module Data.Aeson.Types
9797import Data.Aeson.Types.Generic ()
9898import Data.Aeson.Types.Instances
9999import Data.Aeson.Types.Internal
100+import Data.Aeson.Encoding (Encoding, unsafeToEncoding, fromEncoding, Series, pairs)
100101
101102import Data.Foldable (Foldable, toList)
102−import Data.Aeson.Encode.Functions (brackets)
103103
104104-- | Encode a 'Foldable' as a JSON array.
105105foldable :: (Foldable t, ToJSON a) => t a -> Encoding
106106foldable = toEncoding . toList
107107{-# INLINE foldable #-}
108−
109−-- | Encode a series of key/value pairs, separated by commas.
110−pairs :: Series -> Encoding
111−pairs = brackets '{' '}'
112−{-# INLINE pairs #-}
Data/Aeson/Types/Class.hs+16−17
@@ -1,5 +1,5 @@
1−{-# LANGUAGE CPP, GADTs, DefaultSignatures, FlexibleContexts, DeriveFunctor,
2− ScopedTypeVariables #-}
1+{-# LANGUAGE CPP, GADTs, DefaultSignatures, FlexibleInstances, FlexibleContexts, DeriveFunctor,
2+ ScopedTypeVariables, TypeSynonymInstances #-}
33
44-- |
55-- Module: Data.Aeson.Types.Class
@@ -55,12 +55,12 @@ module Data.Aeson.Types.Class
5555 ) where
5656
5757import Data.Aeson.Types.Internal
58+import Data.Aeson.Encoding.Internal (Encoding, Series)
5859import Data.Monoid ((<>))
5960import Data.Text (Text)
6061import GHC.Generics (Generic, Rep, from, to)
61−import Data.Aeson.Encode.Builder (emptyArray_)
62−import qualified Data.ByteString.Builder as B
63−import qualified Data.Aeson.Encode.Builder as E
62+import qualified Data.Aeson.Encoding.Internal as E
63+import qualified Data.Aeson.Encode.Builder as EB
6464import qualified Data.Vector as V
6565
6666#if !MIN_VERSION_base(4,8,0)
@@ -208,7 +208,7 @@ class ToJSON a where
208208 -- @
209209
210210 toEncoding :: a -> Encoding
211− toEncoding = Encoding . E.encodeToBuilder . toJSON
211+ toEncoding = E.Encoding . EB.encodeToBuilder . toJSON
212212 {-# INLINE toEncoding #-}
213213
214214 toJSONList :: [a] -> Value
@@ -216,12 +216,7 @@ class ToJSON a where
216216 {-# INLINE toJSONList #-}
217217
218218 toEncodingList :: [a] -> Encoding
219− toEncodingList [] = emptyArray_
220− toEncodingList (x:xs) = Encoding $
221− B.char7 '[' <> builder x <> commas xs <> B.char7 ']'
222− where
223− commas = foldr (\v vs -> B.char7 ',' <> builder v <> vs) mempty
224− builder = fromEncoding . toEncoding
219+ toEncodingList = listEncoding toEncoding
225220 {-# INLINE toEncodingList #-}
226221
227222-- | A type that can be converted from JSON, with the possibility of
@@ -325,6 +320,14 @@ class KeyValue kv where
325320 (.=) :: ToJSON v => Text -> v -> kv
326321 infixr 8 .=
327322
323+instance KeyValue Series where
324+ name .= value = E.Value $ E.text name <> E.colon <> toEncoding value
325+ {-# INLINE (.=) #-}
326+
327+instance KeyValue Pair where
328+ name .= value = (name, toJSON value)
329+ {-# INLINE (.=) #-}
330+
328331-------------------------------------------------------------------------------
329332-- Classes and types for map keys
330333-------------------------------------------------------------------------------
@@ -525,11 +528,7 @@ toEncoding2 = liftToEncoding2 toEncoding toEncodingList toEncoding toEncodingLis
525528-------------------------------------------------------------------------------
526529
527530listEncoding :: (a -> Encoding) -> [a] -> Encoding
528−listEncoding _ [] = emptyArray_
529−listEncoding to' (x:xs) = Encoding $
530− B.char7 '[' <> fromEncoding (to' x) <> commas xs <> B.char7 ']'
531− where commas = foldr (\v vs -> B.char7 ',' <> fromEncoding (to' v) <> vs) mempty
532−{-# INLINE listEncoding #-}
531+listEncoding = E.list
533532
534533listValue :: (a -> Value) -> [a] -> Value
535534listValue f = Array . V.fromList . map f
Data/Aeson/Types/Generic.hs+52−57
@@ -24,13 +24,11 @@ module Data.Aeson.Types.Generic ( ) where
2424import Control.Applicative ((<|>))
2525import Control.Monad ((<=<))
2626import Control.Monad.ST (ST)
27−import Data.Aeson.Encode.Builder (emptyArray_)
28−import Data.Aeson.Encode.Functions (builder)
27+import Data.Aeson.Encoding.Internal (Encoding, (>*<))
28+import qualified Data.Aeson.Encoding.Internal as E
2929import Data.Aeson.Types.Instances
3030import Data.Aeson.Types.Internal
3131import Data.Bits (unsafeShiftR)
32−import Data.ByteString.Builder as B
33−import qualified Data.ByteString.Lazy as BSL (null)
3432import Data.DList (DList, toList, empty)
3533import Data.Maybe (fromMaybe)
3634import Data.Monoid ((<>))
@@ -101,28 +99,27 @@ instance (ToJSON a) => GToEncoding (K1 i a) where
10199
102100instance GToEncoding U1 where
103101 -- Empty constructors are encoded to an empty array:
104− gToEncoding _opts _ = emptyArray_
102+ gToEncoding _opts _ = E.emptyArray_
105103
106104instance (ConsToEncoding a) => GToEncoding (C1 c a) where
107105 -- Constructors need to be encoded differently depending on whether they're
108106 -- a record or not. This distinction is made by 'consToEncoding':
109− gToEncoding opts = Encoding . consToEncoding opts . unM1
107+ gToEncoding opts = consToEncoding opts . unM1
110108
111109instance ( EncodeProduct a, EncodeProduct b ) => GToEncoding (a :*: b) where
112110 -- Products are encoded to an array. Here we allocate a mutable vector of
113111 -- the same size as the product and write the product's elements to it using
114112 -- 'encodeProduct':
115− gToEncoding opts p = Encoding $
116− B.char7 '[' <> encodeProduct opts p <> B.char7 ']'
113+ gToEncoding opts p = E.tuple $ encodeProduct opts p
117114
118115instance ( AllNullary (a :+: b) allNullary
119116 , SumToEncoding (a :+: b) allNullary ) => GToEncoding (a :+: b) where
120117 -- If all constructors of a sum datatype are nullary and the
121118 -- 'allNullaryToStringTag' option is set they are encoded to
122119 -- strings. This distinction is made by 'sumToEncoding':
123− gToEncoding opts = Encoding .
124− (unTagged :: Tagged allNullary B.Builder -> B.Builder) .
125− sumToEncoding opts
120+ gToEncoding opts
121+ = (unTagged :: Tagged allNullary Encoding -> Encoding)
122+ . sumToEncoding opts
126123
127124--------------------------------------------------------------------------------
128125
@@ -157,14 +154,14 @@ nonAllNullarySumToJSON opts =
157154--------------------------------------------------------------------------------
158155
159156class SumToEncoding f allNullary where
160− sumToEncoding :: Options -> f a -> Tagged allNullary B.Builder
157+ sumToEncoding :: Options -> f a -> Tagged allNullary Encoding
161158
162159instance ( GetConName f
163160 , TaggedObjectEnc f
164161 , ObjectWithSingleFieldEnc f
165162 , TwoElemArrayEnc f ) => SumToEncoding f True where
166163 sumToEncoding opts
167− | allNullaryToStringTag opts = Tagged . builder .
164+ | allNullaryToStringTag opts = Tagged . toEncoding .
168165 constructorTagModifier opts . getConName
169166 | otherwise = Tagged . nonAllNullarySumToEncoding opts
170167
@@ -176,7 +173,7 @@ instance ( TwoElemArrayEnc f
176173nonAllNullarySumToEncoding :: ( TwoElemArrayEnc f
177174 , TaggedObjectEnc f
178175 , ObjectWithSingleFieldEnc f
179− ) => Options -> f a -> B.Builder
176+ ) => Options -> f a -> Encoding
180177nonAllNullarySumToEncoding opts =
181178 case sumEncoding opts of
182179 TaggedObject{..} ->
@@ -221,7 +218,7 @@ instance (GToJSON f) => TaggedObjectPairs' f False where
221218--------------------------------------------------------------------------------
222219
223220class TaggedObjectEnc f where
224− taggedObjectEnc :: Options -> String -> String -> f a -> B.Builder
221+ taggedObjectEnc :: Options -> String -> String -> f a -> Encoding
225222
226223instance ( TaggedObjectEnc a
227224 , TaggedObjectEnc b ) => TaggedObjectEnc (a :+: b) where
@@ -234,27 +231,27 @@ instance ( IsRecord a isRecord
234231 , TaggedObjectEnc' a isRecord
235232 , Constructor c ) => TaggedObjectEnc (C1 c a) where
236233 taggedObjectEnc opts tagFieldName contentsFieldName v =
237− B.char7 '{' <>
238− (builder tagFieldName <>
239− B.char7 ':' <>
240− builder (constructorTagModifier opts (conName (undefined :: t c a p)))) <>
241− ((unTagged :: Tagged isRecord B.Builder -> B.Builder) .
234+ E.openCurly <>
235+ (toEncoding tagFieldName <>
236+ E.colon <>
237+ toEncoding (constructorTagModifier opts (conName (undefined :: t c a p)))) <>
238+ ((unTagged :: Tagged isRecord Encoding -> Encoding) .
242239 taggedObjectEnc' opts contentsFieldName . unM1 $ v) <>
243− B.char7 '}'
240+ E.closeCurly
244241
245242class TaggedObjectEnc' f isRecord where
246− taggedObjectEnc' :: Options -> String -> f a -> Tagged isRecord B.Builder
243+ taggedObjectEnc' :: Options -> String -> f a -> Tagged isRecord Encoding
247244
248245instance OVERLAPPING_ TaggedObjectEnc' U1 False where
249246 taggedObjectEnc' _ _ _ = Tagged mempty
250247
251248instance (RecordToEncoding f) => TaggedObjectEnc' f True where
252− taggedObjectEnc' opts _ = Tagged . (B.char7 ',' <>) . fst . recordToEncoding opts
249+ taggedObjectEnc' opts _ = Tagged . (E.comma <>) . fst . recordToEncoding opts
253250
254251instance (GToEncoding f) => TaggedObjectEnc' f False where
255252 taggedObjectEnc' opts contentsFieldName =
256− Tagged . (\z -> B.char7 ',' <> builder contentsFieldName <> B.char7 ':' <> z) .
257− gbuilder opts
253+ Tagged . (\z -> E.comma <> toEncoding contentsFieldName <> E.colon <> z) .
254+ gToEncoding opts
258255
259256--------------------------------------------------------------------------------
260257
@@ -290,7 +287,7 @@ instance ( GToJSON a, ConsToJSON a
290287--------------------------------------------------------------------------------
291288
292289class TwoElemArrayEnc f where
293− twoElemArrayEnc :: Options -> f a -> B.Builder
290+ twoElemArrayEnc :: Options -> f a -> Encoding
294291
295292instance (TwoElemArrayEnc a, TwoElemArrayEnc b) => TwoElemArrayEnc (a :+: b) where
296293 twoElemArrayEnc opts (L1 x) = twoElemArrayEnc opts x
@@ -298,9 +295,9 @@ instance (TwoElemArrayEnc a, TwoElemArrayEnc b) => TwoElemArrayEnc (a :+: b) whe
298295
299296instance ( GToEncoding a, ConsToEncoding a
300297 , Constructor c ) => TwoElemArrayEnc (C1 c a) where
301− twoElemArrayEnc opts x = fromEncoding . tuple $
302− builder (constructorTagModifier opts (conName (undefined :: t c a p))) >*<
303− gbuilder opts x
298+ twoElemArrayEnc opts x = E.tuple $
299+ toEncoding (constructorTagModifier opts (conName (undefined :: t c a p))) >*<
300+ gToEncoding opts x
304301
305302--------------------------------------------------------------------------------
306303
@@ -329,15 +326,15 @@ instance GToJSON f => ConsToJSON' f False where
329326--------------------------------------------------------------------------------
330327
331328class ConsToEncoding f where
332− consToEncoding :: Options -> f a -> B.Builder
329+ consToEncoding :: Options -> f a -> Encoding
333330
334331class ConsToEncoding' f isRecord where
335332 consToEncoding' :: Options -> Bool -- ^ Are we a record with one field?
336− -> f a -> Tagged isRecord B.Builder
333+ -> f a -> Tagged isRecord Encoding
337334
338335instance ( IsRecord f isRecord
339336 , ConsToEncoding' f isRecord ) => ConsToEncoding f where
340− consToEncoding opts = (unTagged :: Tagged isRecord B.Builder -> B.Builder)
337+ consToEncoding opts = (unTagged :: Tagged isRecord Encoding -> Encoding)
341338 . consToEncoding' opts (isUnary (undefined :: f a))
342339
343340instance (RecordToEncoding f) => ConsToEncoding' f True where
@@ -345,10 +342,10 @@ instance (RecordToEncoding f) => ConsToEncoding' f True where
345342 let (enc, mbVal) = recordToEncoding opts x
346343 in case (unwrapUnaryRecords opts, isUn, mbVal) of
347344 (True, True, Just val) -> Tagged val
348− _ -> Tagged $ B.char7 '{' <> enc <> B.char7 '}'
345+ _ -> Tagged $ E.wrapObject enc
349346
350347instance GToEncoding f => ConsToEncoding' f False where
351− consToEncoding' opts _ = Tagged . gbuilder opts
348+ consToEncoding' opts _ = Tagged . gToEncoding opts
352349
353350--------------------------------------------------------------------------------
354351
@@ -379,16 +376,16 @@ class RecordToEncoding f where
379376 -- 1st element: whole thing
380377 -- 2nd element: in case the record has only 1 field, just the value
381378 -- of the field (without the key); 'Nothing' otherwise
382− recordToEncoding :: Options -> f a -> (B.Builder, Maybe B.Builder)
379+ recordToEncoding :: Options -> f a -> (Encoding, Maybe Encoding)
383380
384381instance (RecordToEncoding a, RecordToEncoding b) => RecordToEncoding (a :*: b) where
385382 recordToEncoding opts (a :*: b) | omitNothingFields opts =
386− (mconcat $ intersperse (B.char7 ',') $
387− filter (not . BSL.null . B.toLazyByteString)
383+ (mconcat $ intersperse E.comma $
384+ filter (not . E.nullEncoding)
388385 [fst (recordToEncoding opts a), fst (recordToEncoding opts b)]
389386 , Nothing)
390387 recordToEncoding opts (a :*: b) =
391− (fst (recordToEncoding opts a) <> B.char7 ',' <>
388+ (fst (recordToEncoding opts a) <> E.comma <>
392389 fst (recordToEncoding opts b),
393390 Nothing)
394391
@@ -401,11 +398,11 @@ instance OVERLAPPING_ (Selector s, ToJSON a) =>
401398 , K1 Nothing <- k1 = (mempty, Nothing)
402399 recordToEncoding opts m1 = fieldToEncoding opts m1
403400
404−fieldToEncoding :: (Selector s, GToEncoding a) => Options -> S1 s a p -> (B.Builder, Maybe B.Builder)
401+fieldToEncoding :: (Selector s, GToEncoding a) => Options -> S1 s a p -> (Encoding, Maybe Encoding)
405402fieldToEncoding opts m1 =
406− let keyBuilder = builder (fieldLabelModifier opts $ selName m1)
407− valueBuilder = gbuilder opts (unM1 m1)
408− in (keyBuilder <> B.char7 ':' <> valueBuilder, Just valueBuilder)
403+ let keyBuilder = toEncoding (fieldLabelModifier opts $ selName m1)
404+ valueBuilder = gToEncoding opts (unM1 m1)
405+ in (keyBuilder <> E.colon <> valueBuilder, Just valueBuilder)
409406
410407--------------------------------------------------------------------------------
411408
@@ -433,20 +430,20 @@ instance OVERLAPPABLE_ (GToJSON a) => WriteProduct a where
433430--------------------------------------------------------------------------------
434431
435432class EncodeProduct f where
436− encodeProduct :: Options -> f a -> B.Builder
433+ encodeProduct :: Options -> f a -> Encoding
437434
438435instance ( EncodeProduct a
439436 , EncodeProduct b ) => EncodeProduct (a :*: b) where
440437 encodeProduct opts (a :*: b) | omitNothingFields opts =
441− mconcat $ intersperse (B.char7 ',') $
442− filter (not . BSL.null . B.toLazyByteString)
438+ mconcat $ intersperse E.comma $
439+ filter (not . E.nullEncoding)
443440 [encodeProduct opts a, encodeProduct opts b]
444441 encodeProduct opts (a :*: b) = encodeProduct opts a <>
445− B.char7 ',' <>
442+ E.comma <>
446443 encodeProduct opts b
447444
448445instance OVERLAPPABLE_ (GToEncoding a) => EncodeProduct a where
449− encodeProduct opts = gbuilder opts
446+ encodeProduct opts = gToEncoding opts
450447
451448--------------------------------------------------------------------------------
452449
@@ -468,7 +465,7 @@ instance ( GToJSON a, ConsToJSON a
468465--------------------------------------------------------------------------------
469466
470467class ObjectWithSingleFieldEnc f where
471− objectWithSingleFieldEnc :: Options -> f a -> B.Builder
468+ objectWithSingleFieldEnc :: Options -> f a -> Encoding
472469
473470instance ( ObjectWithSingleFieldEnc a
474471 , ObjectWithSingleFieldEnc b ) => ObjectWithSingleFieldEnc (a :+: b) where
@@ -478,15 +475,13 @@ instance ( ObjectWithSingleFieldEnc a
478475instance ( GToEncoding a, ConsToEncoding a
479476 , Constructor c ) => ObjectWithSingleFieldEnc (C1 c a) where
480477 objectWithSingleFieldEnc opts v =
481− B.char7 '{' <>
482− builder (constructorTagModifier opts
483− (conName (undefined :: t c a p))) <>
484− B.char7 ':' <>
485− gbuilder opts v <>
486− B.char7 '}'
487−
488−gbuilder :: GToEncoding f => Options -> f a -> Builder
489−gbuilder opts = fromEncoding . gToEncoding opts
478+ E.openCurly <>
479+ toEncoding
480+ (constructorTagModifier opts
481+ (conName (undefined :: t c a p))) <>
482+ E.colon <>
483+ gToEncoding opts v <>
484+ E.closeCurly
490485
491486--------------------------------------------------------------------------------
492487-- Generic parseJSON
Data/Aeson/Types/Instances.hs+40−41
@@ -78,8 +78,6 @@ module Data.Aeson.Types.Instances
7878 , (.:?)
7979 , (.:!)
8080 , (.!=)
81− , tuple
82− , (>*<)
8381 , typeMismatch
8482
8583 , listEncoding
@@ -87,10 +85,12 @@ module Data.Aeson.Types.Instances
8785 , listParser
8886 ) where
8987
90−import Data.Aeson.Types.Instances.Tuple (tuple, (>*<))
88+import Data.Aeson.Types.Instances.Tuple ()
89+
90+import Data.Aeson.Encoding.Internal (Encoding (..), emptyArray_, dict, tuple, (>*<))
9191
9292import Control.Applicative (Const(..))
93−import Data.Aeson.Encode.Functions (builder, builder', encode, list, encodeWithKey, encodeMap)
93+import Data.Aeson.Encode.Functions (builder)
9494import Data.Aeson.Functions (mapHashKeyVal, mapKey, mapKeyVal)
9595import Data.Aeson.Types.Class
9696import Data.Aeson.Types.Internal
@@ -120,7 +120,7 @@ import Text.ParserCombinators.ReadP (readP_to_S)
120120import Foreign.Storable (Storable)
121121import Numeric.Natural (Natural)
122122import Prelude hiding (foldr)
123−import qualified Data.Aeson.Encode.Builder as E
123+import qualified Data.Aeson.Encode.Builder as EB
124124import qualified Data.Aeson.Parser.Time as Time
125125import qualified Data.ByteString.Builder as B
126126import qualified Data.ByteString.Lazy as L
@@ -201,7 +201,7 @@ realFloatToJSON d
201201
202202realFloatToEncoding :: RealFloat a => a -> Encoding
203203realFloatToEncoding d
204− | isNaN d || isInfinite d = Encoding E.null_
204+ | isNaN d || isInfinite d = Encoding EB.null_
205205 | otherwise = toEncoding (Scientific.fromFloatDigits d)
206206{-# INLINE realFloatToEncoding #-}
207207
@@ -291,7 +291,7 @@ instance ToJSON1 Maybe where
291291 {-# INLINE liftToJSON #-}
292292
293293 liftToEncoding to _ (Just a) = to a
294− liftToEncoding _ _ Nothing = Encoding E.null_
294+ liftToEncoding _ _ Nothing = Encoding EB.null_
295295 {-# INLINE liftToEncoding #-}
296296
297297instance (ToJSON a) => ToJSON (Maybe a) where
@@ -369,7 +369,7 @@ instance ToJSON Bool where
369369 toJSON = Bool
370370 {-# INLINE toJSON #-}
371371
372− toEncoding = Encoding . E.bool
372+ toEncoding = Encoding . EB.bool
373373 {-# INLINE toEncoding #-}
374374
375375instance FromJSON Bool where
@@ -398,7 +398,7 @@ instance ToJSON () where
398398 toJSON _ = emptyArray
399399 {-# INLINE toJSON #-}
400400
401− toEncoding _ = E.emptyArray_
401+ toEncoding _ = emptyArray_
402402 {-# INLINE toEncoding #-}
403403
404404instance FromJSON () where
@@ -415,10 +415,10 @@ instance ToJSON Char where
415415 toJSONList = String . T.pack
416416 {-# INLINE toJSONList #-}
417417
418− toEncoding = Encoding . E.string . (:[])
418+ toEncoding = Encoding . EB.string . (:[])
419419 {-# INLINE toEncoding #-}
420420
421− toEncodingList = Encoding . E.string
421+ toEncodingList = Encoding . EB.string
422422 {-# INLINE toEncodingList #-}
423423
424424instance FromJSON Char where
@@ -490,7 +490,7 @@ instance HasResolution a => ToJSON (Fixed a) where
490490 toJSON = Number . realToFrac
491491 {-# INLINE toJSON #-}
492492
493− toEncoding = Encoding . E.number . realToFrac
493+ toEncoding = Encoding . EB.number . realToFrac
494494 {-# INLINE toEncoding #-}
495495
496496-- | /WARNING:/ Only parse fixed-precision numbers from trusted input
@@ -643,7 +643,7 @@ instance ToJSON Text where
643643 toJSON = String
644644 {-# INLINE toJSON #-}
645645
646− toEncoding = Encoding . E.text
646+ toEncoding = Encoding . EB.text
647647 {-# INLINE toEncoding #-}
648648
649649instance FromJSON Text where
@@ -656,7 +656,7 @@ instance ToJSON LT.Text where
656656
657657 toEncoding t = Encoding $
658658 B.char7 '"' <>
659− LT.foldrChunks (\x xs -> E.unquoted x <> xs) (B.char7 '"') t
659+ LT.foldrChunks (\x xs -> EB.unquoted x <> xs) (B.char7 '"') t
660660 {-# INLINE toEncoding #-}
661661
662662instance FromJSON LT.Text where
@@ -716,7 +716,7 @@ instance ToJSON Scientific where
716716 toJSON = Number
717717 {-# INLINE toJSON #-}
718718
719− toEncoding = Encoding . E.number
719+ toEncoding = Encoding . EB.number
720720 {-# INLINE toEncoding #-}
721721
722722instance FromJSON Scientific where
@@ -823,11 +823,11 @@ instance ToJSONKey k => ToJSON1 (M.Map k) where
823823
824824 -- liftToEncoding :: forall a. (a -> Encoding) -> ([a] -> Encoding) -> M.Map k a -> Encoding
825825 liftToEncoding g _ = case toJSONKey of
826− ToJSONKeyText (_,f) -> encodeMap f g M.minViewWithKey M.foldrWithKey
827− ToJSONKeyValue (_,f) -> list (pairEncoding f) . M.toList
826+ ToJSONKeyText (_,f) -> dict f g M.foldrWithKey
827+ ToJSONKeyValue (_,f) -> listEncoding (pairEncoding f) . M.toList
828828 where
829829 -- pairEncoding :: (k -> Encoding) -> (k, a) -> Encoding
830− pairEncoding f (a, b) = tuple $ fromEncoding (f a) >*< builder' g b
830+ pairEncoding f (a, b) = tuple $ f a >*< g b
831831 {-# INLINE liftToEncoding #-}
832832
833833instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where
@@ -902,7 +902,7 @@ instance ToJSON1 Vector where
902902 {-# INLINE liftToJSON #-}
903903
904904 liftToEncoding to _ xs
905− | V.null xs = E.emptyArray_
905+ | V.null xs = emptyArray_
906906 | otherwise = Encoding $
907907 B.char7 '[' <> fromEncoding (to (V.unsafeHead xs)) <>
908908 V.foldr go (B.char7 ']') (V.unsafeTail xs)
@@ -919,7 +919,7 @@ instance (ToJSON a) => ToJSON (Vector a) where
919919
920920encodeVector :: (ToJSON a, VG.Vector v a) => v a -> Encoding
921921encodeVector xs
922− | VG.null xs = E.emptyArray_
922+ | VG.null xs = emptyArray_
923923 | otherwise = Encoding $
924924 B.char7 '[' <> builder (VG.unsafeHead xs) <>
925925 VG.foldr go (B.char7 ']') (VG.unsafeTail xs)
@@ -1006,11 +1006,11 @@ instance ToJSONKey k => ToJSON1 (H.HashMap k) where
10061006
10071007 -- liftToEncoding :: forall a. (a -> Encoding) -> ([a] -> Encoding) -> H.HashMap k a -> Encoding
10081008 liftToEncoding g _ = case toJSONKey of
1009− ToJSONKeyText (_,f) -> encodeWithKey f g H.foldrWithKey
1010− ToJSONKeyValue (_,f) -> list (pairEncoding f) . H.toList
1009+ ToJSONKeyText (_,f) -> dict f g H.foldrWithKey
1010+ ToJSONKeyValue (_,f) -> listEncoding (pairEncoding f) . H.toList
10111011 where
10121012 -- pairEncoding :: (k -> Encoding) -> (k, a) -> Encoding
1013− pairEncoding f (a, b) = tuple $ fromEncoding (f a) >*< builder' g b
1013+ pairEncoding f (a, b) = tuple $ f a >*< g b
10141014 {-# INLINE liftToEncoding #-}
10151015
10161016instance (ToJSON v, ToJSONKey k) => ToJSON (H.HashMap k v) where
@@ -1047,7 +1047,7 @@ instance ToJSON Value where
10471047 toJSON a = a
10481048 {-# INLINE toJSON #-}
10491049
1050− toEncoding = Encoding . E.encodeToBuilder
1050+ toEncoding = Encoding . EB.encodeToBuilder
10511051 {-# INLINE toEncoding #-}
10521052
10531053instance FromJSON Value where
@@ -1078,21 +1078,21 @@ instance FromJSON DotNetTime where
10781078
10791079instance ToJSON Day where
10801080 toJSON = stringEncoding
1081− toEncoding z = Encoding (E.quote $ E.day z)
1081+ toEncoding z = Encoding (EB.quote $ EB.day z)
10821082
10831083instance FromJSON Day where
10841084 parseJSON = withText "Day" (Time.run Time.day)
10851085
10861086instance ToJSON TimeOfDay where
10871087 toJSON = stringEncoding
1088− toEncoding z = Encoding (E.quote $ E.timeOfDay z)
1088+ toEncoding z = Encoding (EB.quote $ EB.timeOfDay z)
10891089
10901090instance FromJSON TimeOfDay where
10911091 parseJSON = withText "TimeOfDay" (Time.run Time.timeOfDay)
10921092
10931093instance ToJSON LocalTime where
10941094 toJSON = stringEncoding
1095− toEncoding z = Encoding (E.quote $ E.localTime z)
1095+ toEncoding z = Encoding (EB.quote $ EB.localTime z)
10961096
10971097instance FromJSON LocalTime where
10981098 parseJSON = withText "LocalTime" (Time.run Time.localTime)
@@ -1100,7 +1100,7 @@ instance FromJSON LocalTime where
11001100instance ToJSON ZonedTime where
11011101 toJSON = stringEncoding
11021102
1103− toEncoding z = Encoding (E.quote $ E.zonedTime z)
1103+ toEncoding z = Encoding (EB.quote $ EB.zonedTime z)
11041104
11051105formatMillis :: (FormatTime t) => t -> String
11061106formatMillis = take 3 . formatTime defaultTimeLocale "%q"
@@ -1111,11 +1111,19 @@ instance FromJSON ZonedTime where
11111111instance ToJSON UTCTime where
11121112 toJSON = stringEncoding
11131113
1114− toEncoding t = Encoding (E.quote $ E.utcTime t)
1114+ toEncoding t = Encoding (EB.quote $ EB.utcTime t)
11151115
11161116-- | Encode something to a JSON string.
1117+--
1118+-- TODO: remove me, this is a hack
11171119stringEncoding :: (ToJSON a) => a -> Value
1118−stringEncoding = String . T.dropAround (== '"') . T.decodeLatin1 . L.toStrict . encode
1120+stringEncoding = String
1121+ . T.dropAround (== '"')
1122+ . T.decodeLatin1
1123+ . L.toStrict
1124+ . B.toLazyByteString
1125+ . fromEncoding
1126+ . toEncoding
11191127{-# INLINE stringEncoding #-}
11201128
11211129instance FromJSON UTCTime where
@@ -1125,7 +1133,7 @@ instance ToJSON NominalDiffTime where
11251133 toJSON = Number . realToFrac
11261134 {-# INLINE toJSON #-}
11271135
1128− toEncoding = Encoding . E.number . realToFrac
1136+ toEncoding = Encoding . EB.number . realToFrac
11291137 {-# INLINE toEncoding #-}
11301138
11311139-- | /WARNING:/ Only parse lengths of time from trusted input
@@ -1216,7 +1224,7 @@ instance ToJSON (Proxy a) where
12161224 toJSON _ = Null
12171225 {-# INLINE toJSON #-}
12181226
1219− toEncoding _ = Encoding E.null_
1227+ toEncoding _ = Encoding EB.null_
12201228 {-# INLINE toEncoding #-}
12211229
12221230instance FromJSON (Proxy a) where
@@ -1367,15 +1375,6 @@ withBool _ f (Bool arr) = f arr
13671375withBool expected _ v = typeMismatch expected v
13681376{-# INLINE withBool #-}
13691377
1370−instance KeyValue Pair where
1371− name .= value = (name, toJSON value)
1372− {-# INLINE (.=) #-}
1373−
1374−instance KeyValue Series where
1375− name .= value = Value . Encoding $
1376− E.text name <> B.char7 ':' <> builder value
1377− {-# INLINE (.=) #-}
1378−
13791378-- | Convert a value from JSON, failing if the types do not match.
13801379fromJSON :: (FromJSON a) => Value -> Result a
13811380fromJSON = parse parseJSON
Data/Aeson/Types/Instances/Tuple.hs+121−134
@@ -10,16 +10,14 @@
1010-- Portability: portable
1111--
1212-- Tuple instances
13−module Data.Aeson.Types.Instances.Tuple (tuple, (>*<)) where
13+module Data.Aeson.Types.Instances.Tuple () where
1414
15−import Data.Aeson.Encode.Functions (builder)
15+import Data.Aeson.Encoding.Internal (tuple, (>*<))
1616import Data.Aeson.Types.Class
1717import Data.Aeson.Types.Internal
18−import Data.Monoid ((<>))
1918
2019import qualified Data.Vector as V
2120import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
22−import qualified Data.ByteString.Builder as B
2321
2422#if MIN_VERSION_base(4,8,0)
2523#else
@@ -29,15 +27,6 @@ import Control.Applicative ((<$>), (<*>))
2927parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
3028parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
3129
32−(>*<) :: B.Builder -> B.Builder -> B.Builder
33−a >*< b = a <> B.char7 ',' <> b
34−{-# INLINE (>*<) #-}
35−infixr 6 >*<
36−
37−tuple :: B.Builder -> Encoding
38−tuple b = Encoding (B.char7 '[' <> b <> B.char7 ']')
39−{-# INLINE tuple #-}
40−
4130-- Local copy of withArray
4231withArray :: String -> (Array -> Parser a) -> Value -> Parser a
4332withArray _ f (Array arr) = f arr
@@ -57,8 +46,8 @@ instance ToJSON2 ((,) ) where
5746 {-# INLINE liftToJSON2 #-}
5847
5948 liftToEncoding2 toA _ toB _ (a, b) = tuple $
60− fromEncoding (toA a) >*<
61− fromEncoding (toB b)
49+ toA a >*<
50+ toB b
6251 {-# INLINE liftToEncoding2 #-}
6352
6453instance (ToJSON a) => ToJSON1 ((,) a) where
@@ -102,9 +91,9 @@ instance (ToJSON a) => ToJSON2 ((,,) a) where
10291 {-# INLINE liftToJSON2 #-}
10392
10493 liftToEncoding2 toB _ toC _ (a, b, c) = tuple $
105− builder a >*<
106− fromEncoding (toB b) >*<
107− fromEncoding (toC c)
94+ toEncoding a >*<
95+ toB b >*<
96+ toC c
10897 {-# INLINE liftToEncoding2 #-}
10998
11099instance (ToJSON a, ToJSON b) => ToJSON1 ((,,) a b) where
@@ -150,10 +139,10 @@ instance (ToJSON a, ToJSON b) => ToJSON2 ((,,,) a b) where
150139 {-# INLINE liftToJSON2 #-}
151140
152141 liftToEncoding2 toC _ toD _ (a, b, c, d) = tuple $
153− builder a >*<
154− builder b >*<
155− fromEncoding (toC c) >*<
156− fromEncoding (toD d)
142+ toEncoding a >*<
143+ toEncoding b >*<
144+ toC c >*<
145+ toD d
157146 {-# INLINE liftToEncoding2 #-}
158147
159148instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON1 ((,,,) a b c) where
@@ -201,11 +190,11 @@ instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON2 ((,,,,) a b c) where
201190 {-# INLINE liftToJSON2 #-}
202191
203192 liftToEncoding2 toD _ toE _ (a, b, c, d, e) = tuple $
204− builder a >*<
205− builder b >*<
206− builder c >*<
207− fromEncoding (toD d) >*<
208− fromEncoding (toE e)
193+ toEncoding a >*<
194+ toEncoding b >*<
195+ toEncoding c >*<
196+ toD d >*<
197+ toE e
209198 {-# INLINE liftToEncoding2 #-}
210199
211200instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON1 ((,,,,) a b c d) where
@@ -255,12 +244,12 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON2 ((,,,,,) a b c d) w
255244 {-# INLINE liftToJSON2 #-}
256245
257246 liftToEncoding2 toE _ toF _ (a, b, c, d, e, f) = tuple $
258− builder a >*<
259− builder b >*<
260− builder c >*<
261− builder d >*<
262− fromEncoding (toE e) >*<
263− fromEncoding (toF f)
247+ toEncoding a >*<
248+ toEncoding b >*<
249+ toEncoding c >*<
250+ toEncoding d >*<
251+ toE e >*<
252+ toF f
264253 {-# INLINE liftToEncoding2 #-}
265254
266255instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON1 ((,,,,,) a b c d e) where
@@ -312,13 +301,13 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON2 ((,,,,,,)
312301 {-# INLINE liftToJSON2 #-}
313302
314303 liftToEncoding2 toF _ toG _ (a, b, c, d, e, f, g) = tuple $
315− builder a >*<
316− builder b >*<
317− builder c >*<
318− builder d >*<
319− builder e >*<
320− fromEncoding (toF f) >*<
321− fromEncoding (toG g)
304+ toEncoding a >*<
305+ toEncoding b >*<
306+ toEncoding c >*<
307+ toEncoding d >*<
308+ toEncoding e >*<
309+ toF f >*<
310+ toG g
322311 {-# INLINE liftToEncoding2 #-}
323312
324313instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON1 ((,,,,,,) a b c d e f) where
@@ -372,14 +361,14 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON2
372361 {-# INLINE liftToJSON2 #-}
373362
374363 liftToEncoding2 toG _ toH _ (a, b, c, d, e, f, g, h) = tuple $
375− builder a >*<
376− builder b >*<
377− builder c >*<
378− builder d >*<
379− builder e >*<
380− builder f >*<
381− fromEncoding (toG g) >*<
382− fromEncoding (toH h)
364+ toEncoding a >*<
365+ toEncoding b >*<
366+ toEncoding c >*<
367+ toEncoding d >*<
368+ toEncoding e >*<
369+ toEncoding f >*<
370+ toG g >*<
371+ toH h
383372 {-# INLINE liftToEncoding2 #-}
384373
385374instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g) => ToJSON1 ((,,,,,,,) a b c d e f g) where
@@ -435,15 +424,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g)
435424 {-# INLINE liftToJSON2 #-}
436425
437426 liftToEncoding2 toH _ toI _ (a, b, c, d, e, f, g, h, i) = tuple $
438− builder a >*<
439− builder b >*<
440− builder c >*<
441− builder d >*<
442− builder e >*<
443− builder f >*<
444− builder g >*<
445− fromEncoding (toH h) >*<
446− fromEncoding (toI i)
427+ toEncoding a >*<
428+ toEncoding b >*<
429+ toEncoding c >*<
430+ toEncoding d >*<
431+ toEncoding e >*<
432+ toEncoding f >*<
433+ toEncoding g >*<
434+ toH h >*<
435+ toI i
447436 {-# INLINE liftToEncoding2 #-}
448437
449438instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h) => ToJSON1 ((,,,,,,,,) a b c d e f g h) where
@@ -501,16 +490,16 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
501490 {-# INLINE liftToJSON2 #-}
502491
503492 liftToEncoding2 toI _ toJ _ (a, b, c, d, e, f, g, h, i, j) = tuple $
504− builder a >*<
505− builder b >*<
506− builder c >*<
507− builder d >*<
508− builder e >*<
509− builder f >*<
510− builder g >*<
511− builder h >*<
512− fromEncoding (toI i) >*<
513− fromEncoding (toJ j)
493+ toEncoding a >*<
494+ toEncoding b >*<
495+ toEncoding c >*<
496+ toEncoding d >*<
497+ toEncoding e >*<
498+ toEncoding f >*<
499+ toEncoding g >*<
500+ toEncoding h >*<
501+ toI i >*<
502+ toJ j
514503 {-# INLINE liftToEncoding2 #-}
515504
516505instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i) => ToJSON1 ((,,,,,,,,,) a b c d e f g h i) where
@@ -570,17 +559,17 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
570559 {-# INLINE liftToJSON2 #-}
571560
572561 liftToEncoding2 toJ _ toK _ (a, b, c, d, e, f, g, h, i, j, k) = tuple $
573− builder a >*<
574− builder b >*<
575− builder c >*<
576− builder d >*<
577− builder e >*<
578− builder f >*<
579− builder g >*<
580− builder h >*<
581− builder i >*<
582− fromEncoding (toJ j) >*<
583− fromEncoding (toK k)
562+ toEncoding a >*<
563+ toEncoding b >*<
564+ toEncoding c >*<
565+ toEncoding d >*<
566+ toEncoding e >*<
567+ toEncoding f >*<
568+ toEncoding g >*<
569+ toEncoding h >*<
570+ toEncoding i >*<
571+ toJ j >*<
572+ toK k
584573 {-# INLINE liftToEncoding2 #-}
585574
586575instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j) => ToJSON1 ((,,,,,,,,,,) a b c d e f g h i j) where
@@ -642,18 +631,18 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
642631 {-# INLINE liftToJSON2 #-}
643632
644633 liftToEncoding2 toK _ toL _ (a, b, c, d, e, f, g, h, i, j, k, l) = tuple $
645− builder a >*<
646− builder b >*<
647− builder c >*<
648− builder d >*<
649− builder e >*<
650− builder f >*<
651− builder g >*<
652− builder h >*<
653− builder i >*<
654− builder j >*<
655− fromEncoding (toK k) >*<
656− fromEncoding (toL l)
634+ toEncoding a >*<
635+ toEncoding b >*<
636+ toEncoding c >*<
637+ toEncoding d >*<
638+ toEncoding e >*<
639+ toEncoding f >*<
640+ toEncoding g >*<
641+ toEncoding h >*<
642+ toEncoding i >*<
643+ toEncoding j >*<
644+ toK k >*<
645+ toL l
657646 {-# INLINE liftToEncoding2 #-}
658647
659648instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k) => ToJSON1 ((,,,,,,,,,,,) a b c d e f g h i j k) where
@@ -717,19 +706,19 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
717706 {-# INLINE liftToJSON2 #-}
718707
719708 liftToEncoding2 toL _ toM _ (a, b, c, d, e, f, g, h, i, j, k, l, m) = tuple $
720− builder a >*<
721− builder b >*<
722− builder c >*<
723− builder d >*<
724− builder e >*<
725− builder f >*<
726− builder g >*<
727− builder h >*<
728− builder i >*<
729− builder j >*<
730− builder k >*<
731− fromEncoding (toL l) >*<
732− fromEncoding (toM m)
709+ toEncoding a >*<
710+ toEncoding b >*<
711+ toEncoding c >*<
712+ toEncoding d >*<
713+ toEncoding e >*<
714+ toEncoding f >*<
715+ toEncoding g >*<
716+ toEncoding h >*<
717+ toEncoding i >*<
718+ toEncoding j >*<
719+ toEncoding k >*<
720+ toL l >*<
721+ toM m
733722 {-# INLINE liftToEncoding2 #-}
734723
735724instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l) => ToJSON1 ((,,,,,,,,,,,,) a b c d e f g h i j k l) where
@@ -795,20 +784,20 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
795784 {-# INLINE liftToJSON2 #-}
796785
797786 liftToEncoding2 toM _ toN _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n) = tuple $
798− builder a >*<
799− builder b >*<
800− builder c >*<
801− builder d >*<
802− builder e >*<
803− builder f >*<
804− builder g >*<
805− builder h >*<
806− builder i >*<
807− builder j >*<
808− builder k >*<
809− builder l >*<
810− fromEncoding (toM m) >*<
811− fromEncoding (toN n)
787+ toEncoding a >*<
788+ toEncoding b >*<
789+ toEncoding c >*<
790+ toEncoding d >*<
791+ toEncoding e >*<
792+ toEncoding f >*<
793+ toEncoding g >*<
794+ toEncoding h >*<
795+ toEncoding i >*<
796+ toEncoding j >*<
797+ toEncoding k >*<
798+ toEncoding l >*<
799+ toM m >*<
800+ toN n
812801 {-# INLINE liftToEncoding2 #-}
813802
814803instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m) => ToJSON1 ((,,,,,,,,,,,,,) a b c d e f g h i j k l m) where
@@ -876,21 +865,21 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
876865 {-# INLINE liftToJSON2 #-}
877866
878867 liftToEncoding2 toN _ toO _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = tuple $
879− builder a >*<
880− builder b >*<
881− builder c >*<
882− builder d >*<
883− builder e >*<
884− builder f >*<
885− builder g >*<
886− builder h >*<
887− builder i >*<
888− builder j >*<
889− builder k >*<
890− builder l >*<
891− builder m >*<
892− fromEncoding (toN n) >*<
893− fromEncoding (toO o)
868+ toEncoding a >*<
869+ toEncoding b >*<
870+ toEncoding c >*<
871+ toEncoding d >*<
872+ toEncoding e >*<
873+ toEncoding f >*<
874+ toEncoding g >*<
875+ toEncoding h >*<
876+ toEncoding i >*<
877+ toEncoding j >*<
878+ toEncoding k >*<
879+ toEncoding l >*<
880+ toEncoding m >*<
881+ toN n >*<
882+ toO o
894883 {-# INLINE liftToEncoding2 #-}
895884
896885instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m, ToJSON n) => ToJSON1 ((,,,,,,,,,,,,,,) a b c d e f g h i j k l m n) where
@@ -935,5 +924,3 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f
935924instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l, FromJSON m, FromJSON n, FromJSON o) => FromJSON (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) where
936925 parseJSON = parseJSON2
937926 {-# INLINE parseJSON #-}
938−
939−
Data/Aeson/Types/Internal.hs−50
@@ -24,9 +24,6 @@ module Data.Aeson.Types.Internal
2424 (
2525 -- * Core JSON types
2626 Value(..)
27− , Encoding(..)
28− , unsafeToEncoding
29− , Series(..)
3027 , Array
3128 , emptyArray, isEmptyArray
3229 , Pair
@@ -67,7 +64,6 @@ import Control.Applicative
6764import Control.DeepSeq (NFData(..))
6865import Control.Monad (MonadPlus(..), ap)
6966import qualified Control.Monad.Fail as Fail
70−import Data.ByteString.Builder (Builder, char7, toLazyByteString)
7167import Data.Char (isLower, isUpper, toLower, isAlpha, isAlphaNum)
7268import Data.Data (Data)
7369import Data.Foldable (foldl')
@@ -338,52 +334,6 @@ data Value = Object !Object
338334 | Null
339335 deriving (Eq, Read, Show, Typeable, Data)
340336
341−-- | An encoding of a JSON value.
342−newtype Encoding = Encoding {
343− fromEncoding :: Builder
344− -- ^ Acquire the underlying bytestring builder.
345− } deriving (Semigroup,Monoid)
346−
347−-- | Make Encoding from Builder.
348−--
349−-- Use with care! You have to make sure that the passed Builder
350−-- is a valid JSON Encoding!
351−unsafeToEncoding :: Builder -> Encoding
352−unsafeToEncoding = Encoding
353−
354−instance Show Encoding where
355− show (Encoding e) = show (toLazyByteString e)
356−
357−instance Eq Encoding where
358− Encoding a == Encoding b = toLazyByteString a == toLazyByteString b
359−
360−instance Ord Encoding where
361− compare (Encoding a) (Encoding b) =
362− compare (toLazyByteString a) (toLazyByteString b)
363−
364−-- | A series of values that, when encoded, should be separated by
365−-- commas. Since 0.11.0.0, the '.=' operator is overloaded to create
366−-- either @(Text, Value)@ or 'Series'. You can use Series when
367−-- encoding directly to a bytestring builder as in the following
368−-- example:
369−--
370−-- > toEncoding (Person name age) = pairs ("name" .= name <> "age" .= age)
371−data Series = Empty
372− | Value Encoding
373− deriving (Typeable)
374−
375−instance Semigroup Series where
376− Empty <> a = a
377− Value a <> b =
378− Value $
379− a <> case b of
380− Empty -> mempty
381− Value c -> Encoding (char7 ',') <> c
382−
383−instance Monoid Series where
384− mempty = Empty
385− mappend = (<>)
386−
387337-- | A newtype wrapper for 'UTCTime' that uses the same non-standard
388338-- serialization format as Microsoft .NET, whose
389339-- <https://msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx System.DateTime>
aeson.cabal+2
@@ -70,6 +70,8 @@ library
7070 exposed-modules:
7171 Data.Aeson
7272 Data.Aeson.Encode
73+ Data.Aeson.Encoding
74+ Data.Aeson.Encoding.Internal
7375 Data.Aeson.Internal
7476 Data.Aeson.Internal.Time
7577 Data.Aeson.Parser
benchmarks/aeson-benchmarks.cabal+2
@@ -15,6 +15,8 @@ library
1515
1616 exposed-modules:
1717 Data.Aeson
18+ Data.Aeson.Encoding
19+ Data.Aeson.Encoding.Internal
1820 Data.Aeson.Encode
1921 Data.Aeson.Encode.Builder
2022 Data.Aeson.Encode.Functions
tuple-instances.hs+4−4
@@ -1,5 +1,5 @@
11#!/usr/bin/env stack
2−{- stack --resolver=lts-5.9 runghc
2+{- stack --resolver=lts-6.0 runghc
33 --package mtl
44 --package dlist
55-}
@@ -45,9 +45,9 @@ tuple n = fold $ flip execState DList.empty $ do
4545 t [" liftToEncoding2 ", toPrev, " _ ", toLast, " _ ", vs', " = tuple $" ]
4646 forM_ (zip [0..] vs) $ \(i, v) -> t . (" " :) $ case i of
4747
48− _ | i == n - 1 -> [ "fromEncoding (", toLast, " ", v, ")" ]
49− _ | i == n - 2 -> [ "fromEncoding (", toPrev, " ", v, ") >*<" ]
50− _ | otherwise -> [ "builder ", v, " >*<" ]
48+ _ | i == n - 1 -> [ toLast, " ", v ]
49+ _ | i == n - 2 -> [ toPrev, " ", v, " >*<" ]
50+ _ | otherwise -> [ "toEncoding ", v, " >*<" ]
5151 t [" {-# INLINE liftToEncoding2 #-}" ]
5252 t []
5353