3
votes

I made a newtype UUID in my application to represent Text ids.

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

...

newtype UUID =
    UUID Text
      deriving (Eq, Generic, FromJSON, ToJSON, FromField, ToField, FromText, Show, Read, Hashable)

My application needs an instance of FromJSON for HashMap UUID a. HashMap defines an instance for HashMap Text a, is there a way to use it when defining mine?

If I have to redefine the instance, how would I write it? Here's the equivalent from Data.Aeson.Types.Instances:

instance (FromJSON v) => FromJSON (H.HashMap Text v) where
    parseJSON = withObject "HashMap Text a" $ H.traverseWithKey (\k v -> parseJSON v <?> Key k)

How would you write this for UUID? Where are Key and <?> defined, and how can I look that up easily on my own? Hoogle didn't seem to help.

1
It looks like <?> is defined in Data.Aeson.Types.Internal, but I had to clone the repository to find it. Is there a better way? Also, that isn't exposed so I can't even use it?Sean Clark Hess

1 Answers

2
votes

Your UUID is a newtype so you have

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Coerce
-- other imports...

instance (FromJSON v) => FromJSON (HashMap UUID v) where
  parseJSON = coerce (parseJSON :: Value -> Parser (HashMap Text v)) 

Or, perhaps the simpler way

  parseJSON = fmap (fromList . map (\(x,y) -> (UUID x,y)) . toList) . parseJSON

Which simply parses the Text HashMap, then converts it to one indexed on UUID. However, the coerce version is better since it has no runtime cost.