Search code examples
haskellaesontemplate-haskell

No Store instance for Aeson object


The code below generates orphan instances for store. It works fine for aeson 1.4.7.1 but generates error in aeson 2.0.3.0.

{-# LANGUAGE DeriveDataTypeable,DeriveGeneric,TemplateHaskell,GeneralizedNewtypeDeriving,        OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

import           Data.Store      (Store)
import           TH.Derive
import           Data.Scientific
import           Data.Aeson (Value)

-- This will allow us to serialize Aeson objects
$($(derive [d| instance Deriving (Store (Scientific)) |]))
$($(derive [d| instance Deriving (Store (Value)) |])) -- Template haskell derivation of Store    instance for Value

With aeson 2.0.3.0 (as listed in stackage LTS-20.13 - working code was compiled using LTS-16.31 with store 0.7.1 and aeson 1.4.7.1), it now throws errors about some store instance missing for aeson internal object - top level error below:

No instance for (Store
                         aeson-2.0.3.0:Data.Aeson.Types.Internal.Object)
        arising from a use of ‘store-0.7.16:Data.Store.Impl.size’

Will appreciate pointers on how to fix this.

Update

Added the following template haskell derivations (along with ScopedTypeVariable pragma to compile) following suggestions of @daniel-wagner:

$($(derive [d| instance Deriving (Store (Key)) |])) 
$($(derive [d|
    instance Store a => Deriving (Store (KeyMap a))
    |]))

Solution

  • Looks like Object (which isn't actually an internal-only data structure; it's also exported from Data.Aeson.Types with no .Internal) has changed from being a type alias for HashMap (which has a Store instance) to being an abstract data structure (that does not have an instance). You'll need to write Store instances for the Key and KeyMap types, then you can be on your way. You might, for example, use toList and fromList for a programmer-cheap implementation for KeyMap:

    instance Store v => Store (KeyMap v) where
        size = contramap toList size
        poke = contramap toList poke
        peek = fmap fromList peek
    

    And similarly toText and fromText for Key:

    instance Store Key where
        size = contramap toText size
        poke = contramap toText poke
        peek = fmap fromText peek
    

    If you are writing a library, you might want to shove these instances into their own package that is somehow easily discoverable on Hackage -- say, by naming it store-aeson or something -- to reduce the pain of orphan instances. If you're just writing an application, you're probably fine. (I dream of there one day being a searchable orphan instance registry... one of these days, in my copious free time...)