For a while now, I’ve had it in mind to improve the encoding performance of my Haskell JSON package, aeson.
Over the weekend, I went from hazy notion to a proof of concept for what I think could be a reasonable approach.
This post is a case of me “thinking out loud” about the initial design I came up with. I’m very interested in hearing if you have a cleaner idea.
The problem with the encoding method currently used by aeson is that it occurs via a translation to the Value
type. While this is simple and uniform, it involves a large amount of intermediate work that is essentially wasted. When encoding a complex value, the Value
that we build up is expensive, and it will become garbage immediately.
It should be much more efficient to simply serialize straight to a Builder
, the type that is optimized for concatenating many short string fragments. But before marching down that road, I want to make sure that I provide a clean API that is easy to use correctly.
I’ve posted a gist that contains a complete copy of this proof-of-concept code.
{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleInstances,
OverloadedStrings #-}
import Data.Monoid (Monoid(..), (<>))
import Data.Text (Text)
import Data.Text.Lazy.Builder (Builder, singleton)
import qualified Data.Text.Lazy.Builder as Builder
import qualified Data.Text.Lazy.Builder.Int as Builder
The core Build
type has a phantom type that allows us to say “I am encoding a value of type t
”. We’ll see where this type tracking is helpful (and annoying) below.
data Build a = Build {
_count :: !Int
, run :: Builder
}
The internals of the Build
type would be hidden from users; here’s what they mean. The _count
field tracks the number of elements we’re encoding of an aggregate JSON value (an array or object); we’ll see why this matters shortly. The run
field lets us access the underlying Builder
.
We provide three empty types to use as parameters for the Build
type.
data Object
data Array
data Mixed
We’ll want to use the Mixed
type if we’re cramming a set of disparate Haskell values into a JSON array; read on for more.
When it comes to gluing values together, the Monoid
class is exactly what we need.
instance Monoid (Build a) where
mempty = Build 0 mempty
mappend (Build i a) (Build j b)
| ij > 1 = Build ij (a <> singleton ',' <> b)
| otherwise = Build ij (a <> b)
where ij = i + j
Here’s where the _count
field comes in; we want to separate elements of an array or object using commas, but this is necessary only when the array or object contains more than one value.
To encode a simple value, we provide a few obvious helpers. (These are clearly so simple as to be wrong, but remember: my purpose here is to explore the API design, not to provide a proper implementation.)
build :: Builder -> Build a
build = Build 1
int :: Integral a => a -> Build a
int = build . Builder.decimal
text :: Text -> Build Text
text = build . Builder.fromText
Encoding a JSON array is easy.
array :: Build a -> Build Array
array (Build 0 _) = build "[]"
array (Build _ vs) = build $ singleton '[' <> vs <> singleton ']'
If we try this out in ghci
, it behaves as we might hope.
?> array $ int 1 <> int 2
"[1,2]"
JSON puts no constraints on the types of the elements of an array. Unfortunately, our phantom type causes us difficulty here.
An expression of this form will not typecheck, as it’s trying to join a Build Int
with a Build Text
.
?> array $ int 1 <> text "foo"
This is where the Mixed
type from earlier comes in. We use it to forget the original phantom type so that we can construct an array with elements of different types.
mixed :: Build a -> Build Mixed
mixed (Build a b) = Build a b
Our new mixed
function gets the types to be the same, giving us something that typechecks.
?> array $ mixed (int 1) <> mixed (text "foo")
"[1,foo]"
This seems like a fair compromise to me. A Haskell programmer will normally want the types of values in an array to be the same, so the default behaviour of requiring this makes sense (at least to my current thinking), but we get a back door for when we absolutely have to go nuts with mixing types.
The last complication stems from the need to build JSON objects. Each key in an object must be a string, but the value can be of any type.
-- Encode a key-value pair.
(<:>) :: Build Text -> Build a -> Build Object
k <:> v = Build 1 (run k <> ":" <> run v)
object :: Build Object -> Build Object
object (Build 0 _) = build "{}"
object (Build _ kvs) = build $ singleton '{' <> kvs <> singleton '}'
If you’ve had your morning coffee, you’ll notice that I am not living up to my high-minded principles from earlier. Perhaps the types involved here should be something closer to this:
data Object a
(<:>) :: Build Text -> Build a -> Build (Object a)
object :: Build (Object a) -> Build (Object a)
(In which case we’d need a mixed
-like function to forget the phantom types for when we want to get mucky and unsafe—but I digress.)
How does this work out in practice?
?> object $ "foo" <:> int 1 <> "bar" <:> int 3
"{foo:1,bar:3}"
Hey look, that’s more or less as we might have hoped!
Open questions, for which I appeal to you for help:
Does this design appeal to you at all?
If not, what would you change?
If yes, to what extent am I wallowing in the “types for thee, but not for me” sin bin by omitting a phantom parameter for
Object
?
Helpful answers welcome!
I hope you will share something more.
Will Sonbuchner Wife
Look Through More Articles That Are Related. https://smithfieldins.com/chuando-tan-wife-who-is-chuando-tan-is-he-married/
This is very informative and intersting, thanks for sharing.
Paul Inouye Wife
This is the rally nice post thx
Paul inouye wife
Thank you for sharing this valuable information!
Margie Washichek
In this article, we will explore life in-depth & https://eedrich.com/zartprickelnd-net-worth-early-life-and-career-beginnings/
This is very informative and intersting, thanks for sharing: Layla Jenner
Great article! Thanks for this informative article. Keep posting! Visit here – https://eedrich.com/
Thank you for the helpful post!
sophia Hardison
This is the really nice post thx!
Kris Tyson Poster
Just wanted to mention keep up the great work! check out this also. Grayson Murray Fiance Split
Thank you for sharing this post I hope you will share something more: Spm Net Worth
Nice Blog for Readers, check this also https://thepokepoint.com/
Awesome post: Raul Conde Net Worth
I appreciate your useful post: Zach Justice
Thank you for sharing this is really interesting and educational: Mark Kelly Wife
Thank you for the helpful post: Keilani Bautista
This is the really nice post thx: Sandra Janowski
Awesome post: Jeff Tietjens Career
We`ll speak approximately lifestyles in element on this post & https://thepokepoint.com/whiskey-biz-net-worth-how-much-money-is-whiskey-biz-worth/
We’ll talk more about lifestyle in this article See https://thepokepoint.com/danette-tays-the-hollywood-journey-of-a-versatile-actress/
We appreciate you providing us with valuable information. Explore B Simone Real Name
This article will discuss lifestyle in detail. https://viraltimesmagazine.com/betty-anne-waters-net-worth-how-much-money-does-she-make/
Wow that’s incredible: B Simone Net Worth
I really like your blog it is very helpful and informative content good work keep it up: Tokyo Toni Net Worth
This article explains the lifestyle in detail. https://thepokepoint.com/corrie-bird-career-and-personal-life/
Great Post: corrie bird
Wow that’s incredible: Raul Conde Net Worth
This article provides a detailed explanation of the lifestyle. https://rock-heardle.com/lil-mabu-age-bio-career-parents-family-and-personal-achievements/
This is the really nice post thx: Mary Joan Schutz
This article will tell you more about this https://alivelymind.com/ashlyn-peaks-age-bio-height-career-family-and-net-worth-in-2024/
Superb post: Constantine Yankoglu
Thank you very much for this wonderful topic: Margie Washichek Age
This article will let you know greater approximately this Tamara Gilmer
Check out this article for further details. https://alivelymind.com/the-artistic-expression-of-hellstar/
This is the really nice post thx: Constantine Yankoglu
Wow, that’s incredible: Holly Bankemper
For more information, see this article. https://viraltimesmagazine.com/gina-carano-husband-who-is-gina-carano-career-and-personal-life/
Read more about it in this article. https://alivelymind.com/natanael-cano-net-worth-age-biography-career-and-personal-life/
Check check this article to learn more about it. https://thepokepoint.com/payday-loans-eloanwarehouse-key-facts-to-know/
For further information, see this article. https://rock-heardle.com/car-park-cleaning-services-ensuring-a-clean-safe-and-inviting-parking-area/
For further details, check out this article. https://thepokepoint.com/jamie-durie-wealth-how-he-achieved-financial-success/
This is the really nice post thx://kanrus.com/frank-siller-net-worth/
Continue reading this article for more information. https://kanrus.com/lance-gasaway-net-worth/
Ai For Shower Door Business, Artificial intelligence (AI) is revolutionizing industries worldwide, and the shower door industry is no exception.
https://digitalflipermagazine.com/ai-for-shower-door-business-understanding-the-potential-of-ai/
Thank you for sharing this post. I hope you will share something more: Ai For Shower Door Business
Hello, I’m the editor of Vent Speak Magazine, and I couldn’t be more thrilled to welcome you to our platform—a space where voices are amplified, stories are shared, and conversations spark change. At Vent Speak, we pride ourselves on being a haven for raw, authentic expression, offering a stage for diverse perspectives and ideas that often go unheard.
Visit Vent Speak Magazine
DPD Business Cards: DPD sets itself apart in the cutthroat business card manufacturing industry by providing a wide range of customisation choices to suit a wide range of tastes.
https://originalorganiconion.com/
Awesome post: https://ballsymagazine.com/