Экземпляр FromJSON для нового ключа HashMap

Я создал новый тип UUID в своем приложении для представления Text идентификаторов.

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

...

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

Моему приложению нужен экземпляр FromJSON для HashMap UUID a. HashMap определяет экземпляр для HashMap Text a, есть ли способ использовать его при определении моего?

Если мне нужно переопределить экземпляр, как мне его написать? Вот эквивалент из 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)

Как бы вы написали это для UUID? Где определены Key и <?> и как я могу легко найти их самостоятельно? Гугл не помог.


person Sean Clark Hess    schedule 16.12.2015    source источник
comment
Похоже, что ‹?› определено в Data.Aeson.Types.Internal, но мне пришлось клонировать репозиторий, чтобы найти его. Есть ли способ лучше? Кроме того, это не выставлено, поэтому я даже не могу его использовать?   -  person Sean Clark Hess    schedule 16.12.2015


Ответы (1)


Ваш UUID является новым типом, поэтому у вас есть

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Coerce
-- other imports...

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

Или, возможно, более простой способ

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

Который просто анализирует Text HashMap, а затем преобразует его в один, проиндексированный по UUID. Однако версия coerce лучше, так как она не требует затрат времени выполнения.

person user2407038    schedule 16.12.2015
comment
Другой вариант — скопировать исходный код экземпляра FromJSON (HashMap Text a), который, как мне кажется, довольно короткий и простой. Я думаю, что aeson совершил большую ошибку, сделав Object синонимом типа для HashMap Text Value; если бы не это (IMO) неправильное суждение, они могли бы предложить гораздо более приятный instance (FromJSON v, FromJSON k, Hashable k) => FromJSON (HashMap k v). - person dfeuer; 17.12.2015
comment
Похоже, пиар в порядке! - person Sean Clark Hess; 17.12.2015
comment
@dfeuer Текущий экземпляр для Text использует имя типа: withObject "HashMap Text a", поэтому, я думаю, вам также понадобится ограничение Typeable k или подобное. - person user2407038; 18.12.2015
comment
@user2407038 user2407038 Думаю, это проблема (с type Object = HashMap Text Value), потому что авторы хотят оставить значение Object в покое. С гипотетическим newtype Object = Object (HashMap Text Value) этого можно было бы добиться, не исключая общего экземпляра. - person dfeuer; 18.12.2015