json - What is difference between decode and decode' functions from aeson package? -


functions decode , decode' aeson package identical. have subtle difference described in documentation (posting interesting part of docs here):

-- function parses immediately, defers conversion.  see -- 'json' details. decode :: (fromjson a) => l.bytestring -> maybe decode = decodewith jsoneof fromjson  -- function parses , performs conversion immediately.  see -- 'json'' details. decode' :: (fromjson a) => l.bytestring -> maybe decode' = decodewith jsoneof' fromjson 

i tried read description of json , json' functions still don't understand 1 , when should use because documentation not clear enough. can describe more precisely difference between 2 functions , provide example behavior explanation if possible?

update:

there decodestrict , decodestrict' functions. i'm not asking difference between decode' , decodestrict example way interesting question well. what's lazy , what's strict here in these functions not obvious @ all.

the difference between these 2 subtle. there is difference, it’s little complicated. can start taking @ types.

the value type

it’s important note value type aeson provides has been strict long time (specifically, since version 0.4.0.0). means there cannot thunks between constructor of value , internal representation. means bool (and, of course, null) must evaluated once value evaluated whnf.

next, let’s consider string , number. string constructor contains value of type strict text, there can’t laziness there, either. similarly, number constructor contains scientific value, internally represented 2 strict values. both string , number must also evaluated once value evaluated whnf.

we can turn our attention object , array, nontrivial datatypes json provides. these more interesting. objects represented in aeson lazy hashmap. lazy hashmaps evaluate keys whnf, not values, values remain unevaluated thunks. similarly, arrays vectors, not strict in values, either. both of these sorts of values can contain thunks.

with in mind, know that, once have value, only places decode , decode' may differ in production of objects , arrays.

observational differences

the next thing can try evaluate things in ghci , see happens. we’ll start bunch of imports , definitions:

:seti -xoverloadedstrings  import control.exception import control.monad import data.aeson import data.bytestring.lazy (bytestring) import data.list (foldl') import qualified data.hashmap.lazy m import qualified data.vector v  :{ forcespine :: [a] -> io () forcespine = evaluate . foldl' const () :} 

next, let’s parse json:

let jsondocument = "{ \"value\": [1, { \"value\": [2, 3] }] }" :: bytestring  let !parsed = decode jsondocument :: maybe value let !parsed' = decode' jsondocument :: maybe value force parsed force parsed' 

now have 2 bindings, parsed , parsed', 1 of parsed decode , other decode'. forced whnf can @ least see are, can use :sprint command in ghci see how of each value evaluated:

ghci> :sprint parsed parsed = _ ghci> :sprint parsed' parsed' =             (object                (unordered-containers-0.2.8.0:data.hashmap.base.leaf                   15939318180211476069 (data.text.internal.text _ 0 5)                   (array (data.vector.vector 0 2 _)))) 

would @ that! version parsed decode still unevaluated, 1 parsed decode' has data. leads our first meaningful difference between two: decode' forces immediate result whnf, decode defers until needed.

let’s inside these values see if can’t find more differences. happens once evaluate outer objects?

let (just outerobjvalue) = parsed let (just outerobjvalue') = parsed' force outerobjvalue force outerobjvalue'  ghci> :sprint outerobjvalue outerobjvalue = object                   (unordered-containers-0.2.8.0:data.hashmap.base.leaf                      15939318180211476069 (data.text.internal.text _ 0 5)                      (array (data.vector.vector 0 2 _)))  ghci> :sprint outerobjvalue' outerobjvalue' = object                    (unordered-containers-0.2.8.0:data.hashmap.base.leaf                       15939318180211476069 (data.text.internal.text _ 0 5)                       (array (data.vector.vector 0 2 _))) 

this pretty obvious. explicitly forced both of objects, both evaluated hash maps. real question whether or not elements evaluated.

let (array outerarr) = outerobj m.! "value" let (array outerarr') = outerobj' m.! "value" let outerarrlst = v.tolist outerarr let outerarrlst' = v.tolist outerarr'  forcespine outerarrlst forcespine outerarrlst'  ghci> :sprint outerarrlst outerarrlst = [_,_]  ghci> :sprint outerarrlst' outerarrlst' = [number (data.scientific.scientific 1 0),                 object                   (unordered-containers-0.2.8.0:data.hashmap.base.leaf                      15939318180211476069 (data.text.internal.text _ 0 5)                      (array (data.vector.vector 0 2 _)))] 

another difference! array decoded decode, values not forced, ones decoded decode' are. can see, means decode doesn’t perform conversion haskell values until needed, documentation means when says “defers conversion”.

impact

clearly, these 2 functions slightly different, , clearly, decode' stricter decode. what’s meaningful difference, though? when prefer 1 on other?

well, it’s worth mentioning decode never more work decode', decode right default. of course, decode' never more work decode, either, since entire json document needs parsed before value can produced. significant difference decode avoids allocating values if small part of json document used.

of course, laziness not free, either. being lazy means adding thunks, can cost space , time. if of thunks going evaluated, anyway, decode wasting memory , runtime adding useless indirection.

in sense, situations when might want use decode' situations in whole value structure going forced, anyway, dependent on fromjson instance you’re using. in general, wouldn’t worry picking between them unless performance matters and you’re decoding lot of json or doing json decoding in tight loop. in either case, should benchmark. choosing between decode , decode' specific manual optimization, , not feel confident either improve runtime characteristics of program without benchmarks.


Comments

Popular posts from this blog

php - Vagrant up error - Uncaught Reflection Exception: Class DOMDocument does not exist -

vue.js - Create hooks for automated testing -

Add new key value to json node in java -